logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://hacktivis.me/git/pleroma-fe.git
commit: 23dc2d1a5b6b3db7e5daa30c71eda1add2273e34
parent 4a10417ed47cdfe08b2ff4939212116dba3e965f
Author: Henry Jameson <me@hjkos.com>
Date:   Mon, 19 Feb 2024 20:05:49 +0200

refactor ISS stuff into separate file

Diffstat:

Asrc/services/theme_data/iss_utils.js119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/services/theme_data/theme_data_3.service.js128++++++-------------------------------------------------------------------------
2 files changed, 128 insertions(+), 119 deletions(-)

diff --git a/src/services/theme_data/iss_utils.js b/src/services/theme_data/iss_utils.js @@ -0,0 +1,119 @@ +// "Unrolls" a tree structure of item: { parent: { ...item2, parent: { ...item3, parent: {...} } }} +// into an array [item2, item3] for iterating +export const unroll = (item) => { + const out = [] + let currentParent = item + while (currentParent) { + out.push(currentParent) + currentParent = currentParent.parent + } + return out +} + +// This gives you an array of arrays of all possible unique (i.e. order-insensitive) combinations +export const getAllPossibleCombinations = (array) => { + const combos = [array.map(x => [x])] + for (let comboSize = 2; comboSize <= array.length; comboSize++) { + const previous = combos[combos.length - 1] + const selfSet = new Set() + const newCombos = previous.map(self => { + self.forEach(x => selfSet.add(x)) + const nonSelf = array.filter(x => !selfSet.has(x)) + return nonSelf.map(x => [...self, x]) + }) + const flatCombos = newCombos.reduce((acc, x) => [...acc, ...x], []) + combos.push(flatCombos) + } + return combos.reduce((acc, x) => [...acc, ...x], []) +} + +// Converts rule, parents and their criteria into a CSS (or path if ignoreOutOfTreeSelector == true) selector +export const genericRuleToSelector = components => (rule, ignoreOutOfTreeSelector, isParent) => { + if (!rule && !isParent) return null + const component = components[rule.component] + const { states, variants, selector, outOfTreeSelector } = component + + const applicableStates = ((rule.state || []).filter(x => x !== 'normal')).map(state => states[state]) + + const applicableVariantName = (rule.variant || 'normal') + let applicableVariant = '' + if (applicableVariantName !== 'normal') { + applicableVariant = variants[applicableVariantName] + } else { + applicableVariant = variants?.normal ?? '' + } + + let realSelector + if (selector === ':root') { + realSelector = '' + } else if (isParent) { + realSelector = selector + } else { + if (outOfTreeSelector && !ignoreOutOfTreeSelector) realSelector = outOfTreeSelector + else realSelector = selector + } + + const selectors = [realSelector, applicableVariant, ...applicableStates] + .toSorted((a, b) => { + if (a.startsWith(':')) return 1 + if (/^[a-z]/.exec(a)) return -1 + else return 0 + }) + .join('') + + if (rule.parent) { + return (genericRuleToSelector(components)(rule.parent, ignoreOutOfTreeSelector, true) + ' ' + selectors).trim() + } + return selectors.trim() +} + +export const combinationsMatch = (criteria, subject, strict) => { + if (criteria.component !== subject.component) return false + + // All variants inherit from normal + if (subject.variant !== 'normal' || strict) { + if (criteria.variant !== subject.variant) return false + } + + // Subject states > 1 essentially means state is "normal" and therefore matches + if (subject.state.length > 1 || strict) { + const subjectStatesSet = new Set(subject.state) + const criteriaStatesSet = new Set(criteria.state) + + const setsAreEqual = + [...criteriaStatesSet].every(state => subjectStatesSet.has(state)) && + [...subjectStatesSet].every(state => criteriaStatesSet.has(state)) + + if (!setsAreEqual) return false + } + return true +} + +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 + + if (criteria.parent !== undefined && criteria.parent !== null) { + if (!subject.parent && !strict) return true + const pathCriteria = unroll(criteria) + const pathSubject = unroll(subject) + if (pathCriteria.length < pathSubject.length) return false + + // Search: .a .b .c + // Matches: .a .b .c; .b .c; .c; .z .a .b .c + // Does not match .a .b .c .d, .a .b .e + for (let i = 0; i < pathCriteria.length; i++) { + const criteriaParent = pathCriteria[i] + const subjectParent = pathSubject[i] + if (!subjectParent) return true + if (!combinationsMatch(criteriaParent, subjectParent, strict)) return false + } + } + return true +} + +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 @@ -13,6 +13,14 @@ import { process } from './theme3_slot_functions.js' +import { + unroll, + getAllPossibleCombinations, + genericRuleToSelector, + normalizeCombination, + findRules +} from './iss_utils.js' + const DEBUG = false // Ensuring the order of components @@ -109,125 +117,7 @@ componentsContext.keys().forEach(key => { components[component.name] = component }) -// "Unrolls" a tree structure of item: { parent: { ...item2, parent: { ...item3, parent: {...} } }} -// into an array [item2, item3] for iterating -const unroll = (item) => { - const out = [] - let currentParent = item - while (currentParent) { - out.push(currentParent) - currentParent = currentParent.parent - } - return out -} - -// This gives you an array of arrays of all possible unique (i.e. order-insensitive) combinations -export const getAllPossibleCombinations = (array) => { - const combos = [array.map(x => [x])] - for (let comboSize = 2; comboSize <= array.length; comboSize++) { - const previous = combos[combos.length - 1] - const selfSet = new Set() - const newCombos = previous.map(self => { - self.forEach(x => selfSet.add(x)) - const nonSelf = array.filter(x => !selfSet.has(x)) - return nonSelf.map(x => [...self, x]) - }) - const flatCombos = newCombos.reduce((acc, x) => [...acc, ...x], []) - combos.push(flatCombos) - } - return combos.reduce((acc, x) => [...acc, ...x], []) -} - -// Converts rule, parents and their criteria into a CSS (or path if ignoreOutOfTreeSelector == true) selector -export const ruleToSelector = (rule, ignoreOutOfTreeSelector, isParent) => { - if (!rule && !isParent) return null - const component = components[rule.component] - const { states, variants, selector, outOfTreeSelector } = component - - const applicableStates = ((rule.state || []).filter(x => x !== 'normal')).map(state => states[state]) - - const applicableVariantName = (rule.variant || 'normal') - let applicableVariant = '' - if (applicableVariantName !== 'normal') { - applicableVariant = variants[applicableVariantName] - } else { - applicableVariant = variants?.normal ?? '' - } - - let realSelector - if (selector === ':root') { - realSelector = '' - } else if (isParent) { - realSelector = selector - } else { - if (outOfTreeSelector && !ignoreOutOfTreeSelector) realSelector = outOfTreeSelector - else realSelector = selector - } - - const selectors = [realSelector, applicableVariant, ...applicableStates] - .toSorted((a, b) => { - if (a.startsWith(':')) return 1 - if (/^[a-z]/.exec(a)) return -1 - else return 0 - }) - .join('') - - if (rule.parent) { - return (ruleToSelector(rule.parent, ignoreOutOfTreeSelector, true) + ' ' + selectors).trim() - } - return selectors.trim() -} - -const combinationsMatch = (criteria, subject, strict) => { - if (criteria.component !== subject.component) return false - - // All variants inherit from normal - if (subject.variant !== 'normal' || strict) { - if (criteria.variant !== subject.variant) return false - } - - // Subject states > 1 essentially means state is "normal" and therefore matches - if (subject.state.length > 1 || strict) { - const subjectStatesSet = new Set(subject.state) - const criteriaStatesSet = new Set(criteria.state) - - const setsAreEqual = - [...criteriaStatesSet].every(state => subjectStatesSet.has(state)) && - [...subjectStatesSet].every(state => criteriaStatesSet.has(state)) - - if (!setsAreEqual) return false - } - return true -} - -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 - - if (criteria.parent !== undefined && criteria.parent !== null) { - if (!subject.parent && !strict) return true - const pathCriteria = unroll(criteria) - const pathSubject = unroll(subject) - if (pathCriteria.length < pathSubject.length) return false - - // Search: .a .b .c - // Matches: .a .b .c; .b .c; .c; .z .a .b .c - // Does not match .a .b .c .d, .a .b .e - for (let i = 0; i < pathCriteria.length; i++) { - const criteriaParent = pathCriteria[i] - const subjectParent = pathSubject[i] - if (!subjectParent) return true - if (!combinationsMatch(criteriaParent, subjectParent, strict)) return false - } - } - return true -} - -const normalizeCombination = rule => { - rule.variant = rule.variant ?? 'normal' - rule.state = [...new Set(['normal', ...(rule.state || [])])] -} +const ruleToSelector = genericRuleToSelector(components) export const init = (extraRuleset, palette) => { const stacked = {}