aboutsummaryrefslogtreecommitdiff
path: root/src/services/style_setter/style_setter.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/services/style_setter/style_setter.js')
-rw-r--r--src/services/style_setter/style_setter.js442
1 files changed, 34 insertions, 408 deletions
diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js
index 992b3194..46b08628 100644
--- a/src/services/style_setter/style_setter.js
+++ b/src/services/style_setter/style_setter.js
@@ -1,275 +1,13 @@
import { times } from 'lodash'
-import { brightness, invertLightness, convert, contrastRatio } from 'chromatism'
-import { rgb2hex, hex2rgb, mixrgb, getContrastRatio, alphaBlend, alphaBlendLayers } from '../color_convert/color_convert.js'
-
-export const CURRENT_VERSION = 3
-/* This is a definition of all layer combinations
- * each key is a topmost layer, each value represents layer underneath
- * this is essentially a simplified tree
- */
-export const LAYERS = {
- undelay: null, // root
- topBar: null, // no transparency support
- badge: null, // no transparency support
- fg: null,
- bg: 'underlay',
- panel: 'bg',
- btn: 'bg',
- btnPanel: 'panel',
- btnTopBar: 'topBar',
- input: 'bg',
- inputPanel: 'panel',
- inputTopBar: 'topBar',
- alert: 'bg',
- alertPanel: 'panel'
-}
-
-export const SLOT_INHERITANCE = {
- bg: null,
- fg: null,
- text: null,
- underlay: '#000000',
- link: '--accent',
- accent: '--link',
- faint: '--text',
- faintLink: '--link',
-
- cBlue: '#0000ff',
- cRed: '#FF0000',
- cGreen: '#00FF00',
- cOrange: '#E3FF00',
-
- lightBg: {
- depends: ['bg'],
- color: (mod, bg) => brightness(5 * mod, bg).rgb
- },
- lightText: {
- depends: ['text'],
- color: (mod, text) => brightness(20 * mod, text).rgb
- },
-
- border: {
- depends: 'fg',
- color: (mod, fg) => brightness(2 * mod, fg).rgb
- },
-
- linkBg: {
- depends: ['accent', 'bg'],
- color: (mod, accent, bg) => alphaBlend(accent, 0.4, bg).rgb
- },
-
- icon: {
- depends: ['bg', 'text'],
- color: (mod, bg, text) => mixrgb(bg, text)
- },
-
- // Foreground
- fgText: {
- depends: ['text'],
- layer: 'fg',
- textColor: true
- },
- fgLink: {
- depends: ['link'],
- layer: 'fg',
- textColor: 'preserve'
- },
-
- // Panel header
- panel: '--fg',
- panelText: {
- depends: ['fgText'],
- layer: 'panel',
- textColor: true
- },
- panelFaint: {
- depends: ['fgText'],
- layer: 'panel',
- textColor: true
- },
- panelLink: {
- depends: ['fgLink'],
- layer: 'panel',
- textColor: 'preserve'
- },
-
- // Top bar
- topBar: '--fg',
- topBarText: {
- depends: ['fgText'],
- layer: 'topBar',
- textColor: true
- },
- topBarLink: {
- depends: ['fgLink'],
- layer: 'topBar',
- textColor: 'preserve'
- },
-
- // Buttons
- btn: '--fg',
- btnText: {
- depends: ['fgText'],
- layer: 'btn'
- },
- btnPanelText: {
- depends: ['panelText'],
- layer: 'btnPanel',
- variant: 'btn',
- textColor: true
- },
- btnTopBarText: {
- depends: ['topBarText'],
- layer: 'btnTopBar',
- variant: 'btn',
- textColor: true
- },
-
- // Input fields
- input: '--fg',
- inputText: {
- depends: ['text'],
- layer: 'input',
- textColor: true
- },
- inputPanelText: {
- depends: ['panelText'],
- layer: 'inputPanel',
- variant: 'input',
- textColor: true
- },
- inputTopbarText: {
- depends: ['topBarText'],
- layer: 'inputTopBar',
- variant: 'input',
- textColor: true
- },
-
- alertError: '--cRed',
- alertErrorText: {
- depends: ['text', 'alertError'],
- layer: 'alert',
- variant: 'alertError',
- textColor: true
- },
- alertErrorPanelText: {
- depends: ['panelText', 'alertError'],
- layer: 'alertPanel',
- variant: 'alertError',
- textColor: true
- },
-
- alertWarning: '--cOrange',
- alertWarningText: {
- depends: ['text', 'alertWarning'],
- layer: 'alert',
- variant: 'alertWarning',
- textColor: true
- },
- alertWarningPanelText: {
- depends: ['panelText', 'alertWarning'],
- layer: 'alertPanel',
- variant: 'alertWarning',
- textColor: true
- },
-
- badgeNotification: '--cRed',
- badgeNotificationText: {
- depends: ['text', 'badgeNotification'],
- layer: 'badge',
- variant: 'badgeNotification',
- textColor: 'bw'
- }
-}
-
-export const getLayersArray = (layer, data = LAYERS) => {
- let array = [layer]
- let parent = data[layer]
- while (parent) {
- array.unshift(parent)
- parent = data[parent]
- }
- return array
-}
-
-export const getLayers = (layer, variant = layer, colors, opacity) => {
- return getLayersArray(layer).map((currentLayer) => ([
- currentLayer === layer
- ? colors[variant]
- : colors[currentLayer],
- opacity[currentLayer]
- ]))
-}
-
-const getDependencies = (key, inheritance) => {
- const data = inheritance[key]
- if (typeof data === 'string' && data.startsWith('--')) {
- return [data.substring(2)]
- } else {
- if (data === null) return []
- const { depends, layer, variant } = data
- const layerDeps = layer
- ? getLayersArray(layer).map(currentLayer => {
- return currentLayer === layer
- ? variant || layer
- : currentLayer
- })
- : []
- if (Array.isArray(depends)) {
- return [...depends, ...layerDeps]
- } else {
- return [...layerDeps]
- }
- }
-}
-
-export const topoSort = (
- inheritance = SLOT_INHERITANCE,
- getDeps = getDependencies
-) => {
- // This is an implementation of https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
-
- const allKeys = Object.keys(inheritance)
- const whites = new Set(allKeys)
- const grays = new Set()
- const blacks = new Set()
- const unprocessed = [...allKeys]
- const output = []
-
- const step = (node) => {
- if (whites.has(node)) {
- // Make node "gray"
- whites.delete(node)
- grays.add(node)
- // Do step for each node connected to it (one way)
- getDeps(node, inheritance).forEach(step)
- // Make node "black"
- grays.delete(node)
- blacks.add(node)
- // Put it into the output list
- output.push(node)
- } else if (grays.has(node)) {
- console.debug('Cyclic depenency in topoSort, ignoring')
- output.push(node)
- } else if (blacks.has(node)) {
- // do nothing
- } else {
- throw new Error('Unintended condition in topoSort!')
- }
- }
- while (unprocessed.length > 0) {
- step(unprocessed.pop())
- }
- return output
-}
-
-export const SLOT_ORDERED = topoSort(SLOT_INHERITANCE)
+import { convert } from 'chromatism'
+import { rgb2hex, hex2rgb, rgba2css, getCssColor } from '../color_convert/color_convert.js'
+import { getColors } from '../theme_data/theme_data.service.js'
// While this is not used anymore right now, I left it in if we want to do custom
// styles that aren't just colors, so user can pick from a few different distinct
// styles as well as set their own colors in the future.
-const setStyle = (href, commit) => {
+export const setStyle = (href, commit) => {
/***
What's going on here?
I want to make it easy for admins to style this application. To have
@@ -315,30 +53,7 @@ const setStyle = (href, commit) => {
cssEl.addEventListener('load', setDynamic)
}
-const rgb2rgba = function (rgba) {
- return `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})`
-}
-
-const getTextColor = function (bg, text, preserve) {
- const bgIsLight = convert(bg).hsl.l > 50
- const textIsLight = convert(text).hsl.l > 50
-
- console.log(bgIsLight, textIsLight)
-
- if ((bgIsLight && textIsLight) || (!bgIsLight && !textIsLight)) {
- const base = typeof text.a !== 'undefined' ? { a: text.a } : {}
- const result = Object.assign(base, invertLightness(text).rgb)
- if (!preserve && getContrastRatio(bg, result) < 4.5) {
- // B&W
- return contrastRatio(bg, text).rgb
- }
- // Inverted color
- return result
- }
- return text
-}
-
-const applyTheme = (input, commit) => {
+export const applyTheme = (input, commit) => {
const { rules, theme } = generatePreset(input)
const head = document.head
const body = document.body
@@ -399,22 +114,6 @@ const getCssShadowFilter = (input) => {
.join(' ')
}
-const getCssColor = (input, a) => {
- let rgb = {}
- if (typeof input === 'object') {
- rgb = input
- } else if (typeof input === 'string') {
- if (input.startsWith('#')) {
- rgb = hex2rgb(input)
- } else if (input.startsWith('--')) {
- return `var(${input})`
- } else {
- return input
- }
- }
- return rgb2rgba({ ...rgb, a })
-}
-
const generateColors = (themeData) => {
const rawOpacity = Object.assign({
panel: 1,
@@ -435,14 +134,16 @@ const generateColors = (themeData) => {
}, {}))
const inputColors = themeData.colors || themeData
- const transparentsOpacity = Object.entries(inputColors).reduce((acc, [k, v]) => {
- if (v === 'transparent') {
- acc[k] = 0
- }
- return acc
- }, {})
- const opacity = { ...rawOpacity, ...transparentsOpacity }
+ const opacity = {
+ ...rawOpacity,
+ ...Object.entries(inputColors).reduce((acc, [k, v]) => {
+ if (v === 'transparent') {
+ acc[k] = 0
+ }
+ return acc
+ }, {})
+ }
// Cycle one: just whatever we have
const sourceColors = Object.entries(inputColors).reduce((acc, [k, v]) => {
@@ -462,55 +163,7 @@ const generateColors = (themeData) => {
const isLightOnDark = convert(sourceColors.bg).hsl.l < convert(sourceColors.text).hsl.l
const mod = isLightOnDark ? 1 : -1
- const colors = SLOT_ORDERED.reduce((acc, key) => {
- const value = SLOT_INHERITANCE[key]
- if (sourceColors[key]) {
- return { ...acc, [key]: { ...sourceColors[key] } }
- } else if (typeof value === 'string' && value.startsWith('#')) {
- return { ...acc, [key]: convert(value).rgb }
- } else {
- const isObject = typeof value === 'object'
- const defaultColorFunc = (mod, dep) => ({ ...dep })
- const deps = getDependencies(key, SLOT_INHERITANCE)
- const colorFunc = (isObject && value.color) || defaultColorFunc
-
- if (value.textColor) {
- const bg = alphaBlendLayers(
- { ...acc[deps[0]] },
- getLayers(
- value.layer,
- value.variant || value.layer,
- acc,
- opacity
- )
- )
- if (value.textColor === 'bw') {
- return {
- ...acc,
- [key]: contrastRatio(bg)
- }
- } else {
- return {
- ...acc,
- [key]: getTextColor(
- bg,
- { ...acc[deps[0]] },
- value.textColor === 'preserve'
- )
- }
- }
- } else {
- console.log('BENIS', key, deps, deps.map((dep) => ({ ...acc[dep] })))
- return {
- ...acc,
- [key]: colorFunc(
- mod,
- ...deps.map((dep) => ({ ...acc[dep] }))
- )
- }
- }
- }
- }, {})
+ const colors = getColors(sourceColors, opacity, mod)
// Inheriting opacities
Object.entries(opacity).forEach(([ k, v ]) => {
@@ -541,7 +194,7 @@ const generateColors = (themeData) => {
.reduce((acc, [k, v]) => {
if (!v) return acc
acc.solid[k] = rgb2hex(v)
- acc.complete[k] = typeof v.a === 'undefined' ? rgb2hex(v) : rgb2rgba(v)
+ acc.complete[k] = typeof v.a === 'undefined' ? rgb2hex(v) : rgba2css(v)
return acc
}, { complete: {}, solid: {} })
return {
@@ -740,14 +393,12 @@ const composePreset = (colors, radii, shadows, fonts) => {
}
}
-const generatePreset = (input) => {
- const shadows = generateShadows(input)
- const colors = generateColors(input)
- const radii = generateRadii(input)
- const fonts = generateFonts(input)
-
- return composePreset(colors, radii, shadows, fonts)
-}
+const generatePreset = (input) => composePreset(
+ generateColors(input),
+ generateRadii(input),
+ generateShadows(input),
+ generateFonts(input)
+)
const getThemes = () => {
return window.fetch('/static/styles.json')
@@ -779,33 +430,24 @@ const getThemes = () => {
})
}
-const setPreset = (val, commit) => {
+export const setPreset = (val, commit) => {
return getThemes().then((themes) => {
const theme = themes[val] ? themes[val] : themes['pleroma-dark']
const isV1 = Array.isArray(theme)
const data = isV1 ? {} : theme.theme
if (isV1) {
- const bgRgb = hex2rgb(theme[1])
- const fgRgb = hex2rgb(theme[2])
- const textRgb = hex2rgb(theme[3])
- const linkRgb = hex2rgb(theme[4])
-
- const cRedRgb = hex2rgb(theme[5] || '#FF0000')
- const cGreenRgb = hex2rgb(theme[6] || '#00FF00')
- const cBlueRgb = hex2rgb(theme[7] || '#0000FF')
- const cOrangeRgb = hex2rgb(theme[8] || '#E3FF00')
-
- data.colors = {
- bg: bgRgb,
- fg: fgRgb,
- text: textRgb,
- link: linkRgb,
- cRed: cRedRgb,
- cBlue: cBlueRgb,
- cGreen: cGreenRgb,
- cOrange: cOrangeRgb
- }
+ const bg = hex2rgb(theme[1])
+ const fg = hex2rgb(theme[2])
+ const text = hex2rgb(theme[3])
+ const link = hex2rgb(theme[4])
+
+ const cRed = hex2rgb(theme[5] || '#FF0000')
+ const cGreen = hex2rgb(theme[6] || '#00FF00')
+ const cBlue = hex2rgb(theme[7] || '#0000FF')
+ const cOrange = hex2rgb(theme[8] || '#E3FF00')
+
+ data.colors = { bg, fg, text, link, cRed, cBlue, cGreen, cOrange }
}
// This is a hack, this function is only called during initial load.
@@ -819,19 +461,3 @@ const setPreset = (val, commit) => {
}
})
}
-
-export {
- setStyle,
- setPreset,
- applyTheme,
- getTextColor,
- generateColors,
- generateRadii,
- generateShadows,
- generateFonts,
- generatePreset,
- getThemes,
- composePreset,
- getCssShadow,
- getCssShadowFilter
-}