logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://hacktivis.me/git/pleroma-fe.git

iss_utils.js (4600B)


  1. // "Unrolls" a tree structure of item: { parent: { ...item2, parent: { ...item3, parent: {...} } }}
  2. // into an array [item2, item3] for iterating
  3. export const unroll = (item) => {
  4. const out = []
  5. let currentParent = item
  6. while (currentParent) {
  7. out.push(currentParent)
  8. currentParent = currentParent.parent
  9. }
  10. return out
  11. }
  12. // This gives you an array of arrays of all possible unique (i.e. order-insensitive) combinations
  13. // Can only accept primitives. Duplicates are not supported and can cause unexpected behavior
  14. export const getAllPossibleCombinations = (array) => {
  15. const combos = [array.map(x => [x])]
  16. for (let comboSize = 2; comboSize <= array.length; comboSize++) {
  17. const previous = combos[combos.length - 1]
  18. const newCombos = previous.map(self => {
  19. const selfSet = new Set()
  20. self.forEach(x => selfSet.add(x))
  21. const nonSelf = array.filter(x => !selfSet.has(x))
  22. return nonSelf.map(x => [...self, x])
  23. })
  24. const flatCombos = newCombos.reduce((acc, x) => [...acc, ...x], [])
  25. const uniqueComboStrings = new Set()
  26. const uniqueCombos = flatCombos.map(x => x.toSorted()).filter(x => {
  27. if (uniqueComboStrings.has(x.join())) {
  28. return false
  29. } else {
  30. uniqueComboStrings.add(x.join())
  31. return true
  32. }
  33. })
  34. combos.push(uniqueCombos)
  35. }
  36. return combos.reduce((acc, x) => [...acc, ...x], [])
  37. }
  38. // Converts rule, parents and their criteria into a CSS (or path if ignoreOutOfTreeSelector == true) selector
  39. export const genericRuleToSelector = components => (rule, ignoreOutOfTreeSelector, isParent) => {
  40. if (!rule && !isParent) return null
  41. const component = components[rule.component]
  42. const { states = {}, variants = {}, selector, outOfTreeSelector } = component
  43. const applicableStates = ((rule.state || []).filter(x => x !== 'normal')).map(state => states[state])
  44. const applicableVariantName = (rule.variant || 'normal')
  45. let applicableVariant = ''
  46. if (applicableVariantName !== 'normal') {
  47. applicableVariant = variants[applicableVariantName]
  48. } else {
  49. applicableVariant = variants?.normal ?? ''
  50. }
  51. let realSelector
  52. if (selector === ':root') {
  53. realSelector = ''
  54. } else if (isParent) {
  55. realSelector = selector
  56. } else {
  57. if (outOfTreeSelector && !ignoreOutOfTreeSelector) realSelector = outOfTreeSelector
  58. else realSelector = selector
  59. }
  60. const selectors = [realSelector, applicableVariant, ...applicableStates]
  61. .toSorted((a, b) => {
  62. if (a.startsWith(':')) return 1
  63. if (/^[a-z]/.exec(a)) return -1
  64. else return 0
  65. })
  66. .join('')
  67. if (rule.parent) {
  68. return (genericRuleToSelector(components)(rule.parent, ignoreOutOfTreeSelector, true) + ' ' + selectors).trim()
  69. }
  70. return selectors.trim()
  71. }
  72. export const combinationsMatch = (criteria, subject, strict) => {
  73. if (criteria.component !== subject.component) return false
  74. // All variants inherit from normal
  75. if (subject.variant !== 'normal' || strict) {
  76. if (criteria.variant !== subject.variant) return false
  77. }
  78. // Subject states > 1 essentially means state is "normal" and therefore matches
  79. if (subject.state.length > 1 || strict) {
  80. const subjectStatesSet = new Set(subject.state)
  81. const criteriaStatesSet = new Set(criteria.state)
  82. const setsAreEqual =
  83. [...criteriaStatesSet].every(state => subjectStatesSet.has(state)) &&
  84. [...subjectStatesSet].every(state => criteriaStatesSet.has(state))
  85. if (!setsAreEqual) return false
  86. }
  87. return true
  88. }
  89. export const findRules = (criteria, strict) => subject => {
  90. // If we searching for "general" rules - ignore "specific" ones
  91. if (criteria.parent === null && !!subject.parent) return false
  92. if (!combinationsMatch(criteria, subject, strict)) return false
  93. if (criteria.parent !== undefined && criteria.parent !== null) {
  94. if (!subject.parent && !strict) return true
  95. const pathCriteria = unroll(criteria)
  96. const pathSubject = unroll(subject)
  97. if (pathCriteria.length < pathSubject.length) return false
  98. // Search: .a .b .c
  99. // Matches: .a .b .c; .b .c; .c; .z .a .b .c
  100. // Does not match .a .b .c .d, .a .b .e
  101. for (let i = 0; i < pathCriteria.length; i++) {
  102. const criteriaParent = pathCriteria[i]
  103. const subjectParent = pathSubject[i]
  104. if (!subjectParent) return true
  105. if (!combinationsMatch(criteriaParent, subjectParent, strict)) return false
  106. }
  107. }
  108. return true
  109. }
  110. export const normalizeCombination = rule => {
  111. rule.variant = rule.variant ?? 'normal'
  112. rule.state = [...new Set(['normal', ...(rule.state || [])])]
  113. }