Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85628697
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
7 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/services/theme_data/iss_utils.js b/src/services/theme_data/iss_utils.js
index e5aa5cd15c..3dc3aa65dd 100644
--- a/src/services/theme_data/iss_utils.js
+++ b/src/services/theme_data/iss_utils.js
@@ -1,223 +1,222 @@
import { sortBy } from 'lodash'
// "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
// Can only accept primitives. Duplicates are not supported and can cause unexpected behavior
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 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])
})
const flatCombos = newCombos.reduce((acc, x) => [...acc, ...x], [])
const uniqueComboStrings = new Set()
const uniqueCombos = flatCombos.map(sortBy).filter((x) => {
if (uniqueComboStrings.has(x.join())) {
return false
} else {
uniqueComboStrings.add(x.join())
return true
}
})
combos.push(uniqueCombos)
}
return combos.reduce((acc, x) => [...acc, ...x], [])
}
/**
* 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, 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 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
+ return 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
*
* @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
// 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
}
/**
* 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
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
}
// Pre-fills 'normal' state/variant if missing
export const normalizeCombination = (rule) => {
rule.variant = rule.variant ?? 'normal'
rule.state = [...new Set(['normal', ...(rule.state || [])])]
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Aug 8, 1:32 PM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1723511
Default Alt Text
(7 KB)
Attached To
Mode
rPUFE pleroma-fe-upstream
Attached
Detach File
Event Timeline
Log In to Comment