logo

pleroma-fe

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

css_utils.js (5352B)


  1. import { convert } from 'chromatism'
  2. import { hex2rgb, rgba2css } from '../color_convert/color_convert.js'
  3. export const parseCssShadow = (text) => {
  4. const dimensions = /(\d[a-z]*\s?){2,4}/.exec(text)?.[0]
  5. const inset = /inset/.exec(text)?.[0]
  6. const color = text.replace(dimensions, '').replace(inset, '')
  7. const [x, y, blur = 0, spread = 0] = dimensions.split(/ /).filter(x => x).map(x => x.trim())
  8. const isInset = inset?.trim() === 'inset'
  9. const colorString = color.split(/ /).filter(x => x).map(x => x.trim())[0]
  10. return {
  11. x,
  12. y,
  13. blur,
  14. spread,
  15. inset: isInset,
  16. color: colorString
  17. }
  18. }
  19. export const getCssColorString = (color, alpha = 1) => rgba2css({ ...convert(color).rgb, a: alpha })
  20. export const getCssShadow = (input, usesDropShadow) => {
  21. if (input.length === 0) {
  22. return 'none'
  23. }
  24. return input
  25. .filter(_ => usesDropShadow ? _.inset : _)
  26. .map((shad) => [
  27. shad.x,
  28. shad.y,
  29. shad.blur,
  30. shad.spread
  31. ].map(_ => _ + 'px ').concat([
  32. getCssColorString(shad.color, shad.alpha),
  33. shad.inset ? 'inset' : ''
  34. ]).join(' ')).join(', ')
  35. }
  36. export const getCssShadowFilter = (input) => {
  37. if (input.length === 0) {
  38. return 'none'
  39. }
  40. return input
  41. // drop-shadow doesn't support inset or spread
  42. .filter((shad) => !shad.inset && Number(shad.spread) === 0)
  43. .map((shad) => [
  44. shad.x,
  45. shad.y,
  46. // drop-shadow's blur is twice as strong compared to box-shadow
  47. shad.blur / 2
  48. ].map(_ => _ + 'px').concat([
  49. getCssColorString(shad.color, shad.alpha)
  50. ]).join(' '))
  51. .map(_ => `drop-shadow(${_})`)
  52. .join(' ')
  53. }
  54. // `debug` changes what backgrounds are used to "stacked" solid colors so you can see
  55. // what theme engine "thinks" is actual background color is for purposes of text color
  56. // generation and for when --stacked variable is used
  57. export const getCssRules = (rules, debug) => rules.map(rule => {
  58. let selector = rule.selector
  59. if (!selector) {
  60. selector = 'html'
  61. }
  62. const header = selector + ' {'
  63. const footer = '}'
  64. const virtualDirectives = Object.entries(rule.virtualDirectives || {}).map(([k, v]) => {
  65. return ' ' + k + ': ' + v
  66. }).join(';\n')
  67. const directives = Object.entries(rule.directives).map(([k, v]) => {
  68. switch (k) {
  69. case 'roundness': {
  70. return ' ' + [
  71. '--roundness: ' + v + 'px'
  72. ].join(';\n ')
  73. }
  74. case 'shadow': {
  75. return ' ' + [
  76. '--shadow: ' + getCssShadow(rule.dynamicVars.shadow),
  77. '--shadowFilter: ' + getCssShadowFilter(rule.dynamicVars.shadow),
  78. '--shadowInset: ' + getCssShadow(rule.dynamicVars.shadow, true)
  79. ].join(';\n ')
  80. }
  81. case 'background': {
  82. if (debug) {
  83. return `
  84. --background: ${getCssColorString(rule.dynamicVars.stacked)};
  85. background-color: ${getCssColorString(rule.dynamicVars.stacked)};
  86. `
  87. }
  88. if (v === 'transparent') {
  89. if (rule.component === 'Root') return []
  90. return [
  91. rule.directives.backgroundNoCssColor !== 'yes' ? ('background-color: ' + v) : '',
  92. ' --background: ' + v
  93. ].filter(x => x).join(';\n')
  94. }
  95. const color = getCssColorString(rule.dynamicVars.background, rule.directives.opacity)
  96. const cssDirectives = ['--background: ' + color]
  97. if (rule.directives.backgroundNoCssColor !== 'yes') {
  98. cssDirectives.push('background-color: ' + color)
  99. }
  100. return cssDirectives.filter(x => x).join(';\n')
  101. }
  102. case 'blur': {
  103. const cssDirectives = []
  104. if (rule.directives.opacity < 1) {
  105. cssDirectives.push(`--backdrop-filter: blur(${v}) `)
  106. if (rule.directives.backgroundNoCssColor !== 'yes') {
  107. cssDirectives.push(`backdrop-filter: blur(${v}) `)
  108. }
  109. }
  110. return cssDirectives.join(';\n')
  111. }
  112. case 'font': {
  113. return 'font-family: ' + v
  114. }
  115. case 'textColor': {
  116. if (rule.directives.textNoCssColor === 'yes') { return '' }
  117. return 'color: ' + v
  118. }
  119. default:
  120. if (k.startsWith('--')) {
  121. const [type, value] = v.split('|').map(x => x.trim()) // woah, Extreme!
  122. switch (type) {
  123. case 'color': {
  124. const color = rule.dynamicVars[k]
  125. if (typeof color === 'string') {
  126. return k + ': ' + rgba2css(hex2rgb(color))
  127. } else {
  128. return k + ': ' + rgba2css(color)
  129. }
  130. }
  131. case 'generic':
  132. return k + ': ' + value
  133. default:
  134. return ''
  135. }
  136. }
  137. return ''
  138. }
  139. }).filter(x => x).map(x => ' ' + x).join(';\n')
  140. return [
  141. header,
  142. directives + ';',
  143. (rule.component === 'Text' && rule.state.indexOf('faint') < 0 && rule.directives.textNoCssColor !== 'yes') ? ' color: var(--text);' : '',
  144. '',
  145. virtualDirectives,
  146. footer
  147. ].join('\n')
  148. }).filter(x => x)
  149. export const getScopedVersion = (rules, newScope) => {
  150. return rules.map(x => {
  151. if (x.startsWith('html')) {
  152. return x.replace('html', newScope)
  153. } else if (x.startsWith('#content')) {
  154. return x.replace('#content', newScope)
  155. } else {
  156. return newScope + ' > ' + x
  157. }
  158. })
  159. }