diff options
Diffstat (limited to 'src/services/style_setter/style_setter.js')
| -rw-r--r-- | src/services/style_setter/style_setter.js | 442 |
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 -} |
