moved themes2 stuff into theme_data.service.js
This commit is contained in:
parent
c3e6e7c61d
commit
da0db933d9
|
@ -1,6 +1,5 @@
|
||||||
import { convert } from 'chromatism'
|
import { hex2rgb } from '../color_convert/color_convert.js'
|
||||||
import { rgb2hex, hex2rgb, rgba2css, getCssColor, relativeLuminance } from '../color_convert/color_convert.js'
|
import { generatePreset } from '../theme_data/theme_data.service.js'
|
||||||
import { getColors, computeDynamicColor, getOpacitySlot } from '../theme_data/theme_data.service.js'
|
|
||||||
import { init } from '../theme_data/theme_data_3.service.js'
|
import { init } from '../theme_data/theme_data_3.service.js'
|
||||||
import { convertTheme2To3 } from '../theme_data/theme2_to_theme3.js'
|
import { convertTheme2To3 } from '../theme_data/theme2_to_theme3.js'
|
||||||
import { getCssRules } from '../theme_data/css_utils.js'
|
import { getCssRules } from '../theme_data/css_utils.js'
|
||||||
|
@ -83,308 +82,6 @@ export const applyConfig = (config) => {
|
||||||
body.classList.remove('hidden')
|
body.classList.remove('hidden')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getCssShadow = (input, usesDropShadow) => {
|
|
||||||
if (input.length === 0) {
|
|
||||||
return 'none'
|
|
||||||
}
|
|
||||||
|
|
||||||
return input
|
|
||||||
.filter(_ => usesDropShadow ? _.inset : _)
|
|
||||||
.map((shad) => [
|
|
||||||
shad.x,
|
|
||||||
shad.y,
|
|
||||||
shad.blur,
|
|
||||||
shad.spread
|
|
||||||
].map(_ => _ + 'px').concat([
|
|
||||||
getCssColor(shad.color, shad.alpha),
|
|
||||||
shad.inset ? 'inset' : ''
|
|
||||||
]).join(' ')).join(', ')
|
|
||||||
}
|
|
||||||
|
|
||||||
const getCssShadowFilter = (input) => {
|
|
||||||
if (input.length === 0) {
|
|
||||||
return 'none'
|
|
||||||
}
|
|
||||||
|
|
||||||
return input
|
|
||||||
// drop-shadow doesn't support inset or spread
|
|
||||||
.filter((shad) => !shad.inset && Number(shad.spread) === 0)
|
|
||||||
.map((shad) => [
|
|
||||||
shad.x,
|
|
||||||
shad.y,
|
|
||||||
// drop-shadow's blur is twice as strong compared to box-shadow
|
|
||||||
shad.blur / 2
|
|
||||||
].map(_ => _ + 'px').concat([
|
|
||||||
getCssColor(shad.color, shad.alpha)
|
|
||||||
]).join(' '))
|
|
||||||
.map(_ => `drop-shadow(${_})`)
|
|
||||||
.join(' ')
|
|
||||||
}
|
|
||||||
|
|
||||||
export const generateColors = (themeData) => {
|
|
||||||
const sourceColors = !themeData.themeEngineVersion
|
|
||||||
? colors2to3(themeData.colors || themeData)
|
|
||||||
: themeData.colors || themeData
|
|
||||||
|
|
||||||
const { colors, opacity } = getColors(sourceColors, themeData.opacity || {})
|
|
||||||
|
|
||||||
const htmlColors = Object.entries(colors)
|
|
||||||
.reduce((acc, [k, v]) => {
|
|
||||||
if (!v) return acc
|
|
||||||
acc.solid[k] = rgb2hex(v)
|
|
||||||
acc.complete[k] = typeof v.a === 'undefined' ? rgb2hex(v) : rgba2css(v)
|
|
||||||
return acc
|
|
||||||
}, { complete: {}, solid: {} })
|
|
||||||
return {
|
|
||||||
rules: {
|
|
||||||
colors: Object.entries(htmlColors.complete)
|
|
||||||
.filter(([k, v]) => v)
|
|
||||||
.map(([k, v]) => `--${k}: ${v}`)
|
|
||||||
.join(';')
|
|
||||||
},
|
|
||||||
theme: {
|
|
||||||
colors: htmlColors.solid,
|
|
||||||
opacity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const generateRadii = (input) => {
|
|
||||||
let inputRadii = input.radii || {}
|
|
||||||
// v1 -> v2
|
|
||||||
if (typeof input.btnRadius !== 'undefined') {
|
|
||||||
inputRadii = Object
|
|
||||||
.entries(input)
|
|
||||||
.filter(([k, v]) => k.endsWith('Radius'))
|
|
||||||
.reduce((acc, e) => { acc[e[0].split('Radius')[0]] = e[1]; return acc }, {})
|
|
||||||
}
|
|
||||||
const radii = Object.entries(inputRadii).filter(([k, v]) => v).reduce((acc, [k, v]) => {
|
|
||||||
acc[k] = v
|
|
||||||
return acc
|
|
||||||
}, {
|
|
||||||
btn: 4,
|
|
||||||
input: 4,
|
|
||||||
checkbox: 2,
|
|
||||||
panel: 10,
|
|
||||||
avatar: 5,
|
|
||||||
avatarAlt: 50,
|
|
||||||
tooltip: 2,
|
|
||||||
attachment: 5,
|
|
||||||
chatMessage: inputRadii.panel
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
rules: {
|
|
||||||
radii: Object.entries(radii).filter(([k, v]) => v).map(([k, v]) => `--${k}Radius: ${v}px`).join(';')
|
|
||||||
},
|
|
||||||
theme: {
|
|
||||||
radii
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const generateFonts = (input) => {
|
|
||||||
const fonts = Object.entries(input.fonts || {}).filter(([k, v]) => v).reduce((acc, [k, v]) => {
|
|
||||||
acc[k] = Object.entries(v).filter(([k, v]) => v).reduce((acc, [k, v]) => {
|
|
||||||
acc[k] = v
|
|
||||||
return acc
|
|
||||||
}, acc[k])
|
|
||||||
return acc
|
|
||||||
}, {
|
|
||||||
interface: {
|
|
||||||
family: 'sans-serif'
|
|
||||||
},
|
|
||||||
input: {
|
|
||||||
family: 'inherit'
|
|
||||||
},
|
|
||||||
post: {
|
|
||||||
family: 'inherit'
|
|
||||||
},
|
|
||||||
postCode: {
|
|
||||||
family: 'monospace'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
rules: {
|
|
||||||
fonts: Object
|
|
||||||
.entries(fonts)
|
|
||||||
.filter(([k, v]) => v)
|
|
||||||
.map(([k, v]) => `--${k}Font: ${v.family}`).join(';')
|
|
||||||
},
|
|
||||||
theme: {
|
|
||||||
fonts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const border = (top, shadow) => ({
|
|
||||||
x: 0,
|
|
||||||
y: top ? 1 : -1,
|
|
||||||
blur: 0,
|
|
||||||
spread: 0,
|
|
||||||
color: shadow ? '#000000' : '#FFFFFF',
|
|
||||||
alpha: 0.2,
|
|
||||||
inset: true
|
|
||||||
})
|
|
||||||
const buttonInsetFakeBorders = [border(true, false), border(false, true)]
|
|
||||||
const inputInsetFakeBorders = [border(true, true), border(false, false)]
|
|
||||||
const hoverGlow = {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
blur: 4,
|
|
||||||
spread: 0,
|
|
||||||
color: '--faint',
|
|
||||||
alpha: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DEFAULT_SHADOWS = {
|
|
||||||
panel: [{
|
|
||||||
x: 1,
|
|
||||||
y: 1,
|
|
||||||
blur: 4,
|
|
||||||
spread: 0,
|
|
||||||
color: '#000000',
|
|
||||||
alpha: 0.6
|
|
||||||
}],
|
|
||||||
topBar: [{
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
blur: 4,
|
|
||||||
spread: 0,
|
|
||||||
color: '#000000',
|
|
||||||
alpha: 0.6
|
|
||||||
}],
|
|
||||||
popup: [{
|
|
||||||
x: 2,
|
|
||||||
y: 2,
|
|
||||||
blur: 3,
|
|
||||||
spread: 0,
|
|
||||||
color: '#000000',
|
|
||||||
alpha: 0.5
|
|
||||||
}],
|
|
||||||
avatar: [{
|
|
||||||
x: 0,
|
|
||||||
y: 1,
|
|
||||||
blur: 8,
|
|
||||||
spread: 0,
|
|
||||||
color: '#000000',
|
|
||||||
alpha: 0.7
|
|
||||||
}],
|
|
||||||
avatarStatus: [],
|
|
||||||
panelHeader: [],
|
|
||||||
button: [{
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
blur: 2,
|
|
||||||
spread: 0,
|
|
||||||
color: '#000000',
|
|
||||||
alpha: 1
|
|
||||||
}, ...buttonInsetFakeBorders],
|
|
||||||
buttonHover: [hoverGlow, ...buttonInsetFakeBorders],
|
|
||||||
buttonPressed: [hoverGlow, ...inputInsetFakeBorders],
|
|
||||||
input: [...inputInsetFakeBorders, {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
blur: 2,
|
|
||||||
inset: true,
|
|
||||||
spread: 0,
|
|
||||||
color: '#000000',
|
|
||||||
alpha: 1
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
export const generateShadows = (input, colors) => {
|
|
||||||
// TODO this is a small hack for `mod` to work with shadows
|
|
||||||
// this is used to get the "context" of shadow, i.e. for `mod` properly depend on background color of element
|
|
||||||
const hackContextDict = {
|
|
||||||
button: 'btn',
|
|
||||||
panel: 'bg',
|
|
||||||
top: 'topBar',
|
|
||||||
popup: 'popover',
|
|
||||||
avatar: 'bg',
|
|
||||||
panelHeader: 'panel',
|
|
||||||
input: 'input'
|
|
||||||
}
|
|
||||||
|
|
||||||
const cleanInputShadows = Object.fromEntries(
|
|
||||||
Object.entries(input.shadows || {})
|
|
||||||
.map(([name, shadowSlot]) => [
|
|
||||||
name,
|
|
||||||
// defaulting color to black to avoid potential problems
|
|
||||||
shadowSlot.map(shadowDef => ({ color: '#000000', ...shadowDef }))
|
|
||||||
])
|
|
||||||
)
|
|
||||||
const inputShadows = cleanInputShadows && !input.themeEngineVersion
|
|
||||||
? shadows2to3(cleanInputShadows, input.opacity)
|
|
||||||
: cleanInputShadows || {}
|
|
||||||
const shadows = Object.entries({
|
|
||||||
...DEFAULT_SHADOWS,
|
|
||||||
...inputShadows
|
|
||||||
}).reduce((shadowsAcc, [slotName, shadowDefs]) => {
|
|
||||||
const slotFirstWord = slotName.replace(/[A-Z].*$/, '')
|
|
||||||
const colorSlotName = hackContextDict[slotFirstWord]
|
|
||||||
const isLightOnDark = relativeLuminance(convert(colors[colorSlotName]).rgb) < 0.5
|
|
||||||
const mod = isLightOnDark ? 1 : -1
|
|
||||||
const newShadow = shadowDefs.reduce((shadowAcc, def) => [
|
|
||||||
...shadowAcc,
|
|
||||||
{
|
|
||||||
...def,
|
|
||||||
color: rgb2hex(computeDynamicColor(
|
|
||||||
def.color,
|
|
||||||
(variableSlot) => convert(colors[variableSlot]).rgb,
|
|
||||||
mod
|
|
||||||
))
|
|
||||||
}
|
|
||||||
], [])
|
|
||||||
return { ...shadowsAcc, [slotName]: newShadow }
|
|
||||||
}, {})
|
|
||||||
|
|
||||||
return {
|
|
||||||
rules: {
|
|
||||||
shadows: Object
|
|
||||||
.entries(shadows)
|
|
||||||
// TODO for v2.2: if shadow doesn't have non-inset shadows with spread > 0 - optionally
|
|
||||||
// convert all non-inset shadows into filter: drop-shadow() to boost performance
|
|
||||||
.map(([k, v]) => [
|
|
||||||
`--${k}Shadow: ${getCssShadow(v)}`,
|
|
||||||
`--${k}ShadowFilter: ${getCssShadowFilter(v)}`,
|
|
||||||
`--${k}ShadowInset: ${getCssShadow(v, true)}`
|
|
||||||
].join(';'))
|
|
||||||
.join(';')
|
|
||||||
},
|
|
||||||
theme: {
|
|
||||||
shadows
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const composePreset = (colors, radii, shadows, fonts) => {
|
|
||||||
return {
|
|
||||||
rules: {
|
|
||||||
...shadows.rules,
|
|
||||||
...colors.rules,
|
|
||||||
...radii.rules,
|
|
||||||
...fonts.rules
|
|
||||||
},
|
|
||||||
theme: {
|
|
||||||
...shadows.theme,
|
|
||||||
...colors.theme,
|
|
||||||
...radii.theme,
|
|
||||||
...fonts.theme
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const generatePreset = (input) => {
|
|
||||||
const colors = generateColors(input)
|
|
||||||
return composePreset(
|
|
||||||
colors,
|
|
||||||
generateRadii(input),
|
|
||||||
generateShadows(input, colors.theme.colors, colors.mod),
|
|
||||||
generateFonts(input)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getThemes = () => {
|
export const getThemes = () => {
|
||||||
const cache = 'no-store'
|
const cache = 'no-store'
|
||||||
|
|
||||||
|
@ -414,47 +111,6 @@ export const getThemes = () => {
|
||||||
}, {})
|
}, {})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
export const colors2to3 = (colors) => {
|
|
||||||
return Object.entries(colors).reduce((acc, [slotName, color]) => {
|
|
||||||
const btnPositions = ['', 'Panel', 'TopBar']
|
|
||||||
switch (slotName) {
|
|
||||||
case 'lightBg':
|
|
||||||
return { ...acc, highlight: color }
|
|
||||||
case 'btnText':
|
|
||||||
return {
|
|
||||||
...acc,
|
|
||||||
...btnPositions
|
|
||||||
.reduce(
|
|
||||||
(statePositionAcc, position) =>
|
|
||||||
({ ...statePositionAcc, ['btn' + position + 'Text']: color })
|
|
||||||
, {}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return { ...acc, [slotName]: color }
|
|
||||||
}
|
|
||||||
}, {})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This handles compatibility issues when importing v2 theme's shadows to current format
|
|
||||||
*
|
|
||||||
* Back in v2 shadows allowed you to use dynamic colors however those used pure CSS3 variables
|
|
||||||
*/
|
|
||||||
export const shadows2to3 = (shadows, opacity) => {
|
|
||||||
return Object.entries(shadows).reduce((shadowsAcc, [slotName, shadowDefs]) => {
|
|
||||||
const isDynamic = ({ color = '#000000' }) => color.startsWith('--')
|
|
||||||
const getOpacity = ({ color }) => opacity[getOpacitySlot(color.substring(2).split(',')[0])]
|
|
||||||
const newShadow = shadowDefs.reduce((shadowAcc, def) => [
|
|
||||||
...shadowAcc,
|
|
||||||
{
|
|
||||||
...def,
|
|
||||||
alpha: isDynamic(def) ? getOpacity(def) || 1 : def.alpha
|
|
||||||
}
|
|
||||||
], [])
|
|
||||||
return { ...shadowsAcc, [slotName]: newShadow }
|
|
||||||
}, {})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getPreset = (val) => {
|
export const getPreset = (val) => {
|
||||||
return getThemes()
|
return getThemes()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { convert, brightness, contrastRatio } from 'chromatism'
|
import { convert, brightness, contrastRatio } from 'chromatism'
|
||||||
import { alphaBlendLayers, getTextColor, relativeLuminance } from '../color_convert/color_convert.js'
|
import { rgb2hex, rgba2css, alphaBlendLayers, getTextColor, relativeLuminance, getCssColor } from '../color_convert/color_convert.js'
|
||||||
import { LAYERS, DEFAULT_OPACITY, SLOT_INHERITANCE } from './pleromafe.js'
|
import { LAYERS, DEFAULT_OPACITY, SLOT_INHERITANCE } from './pleromafe.js'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -407,3 +407,347 @@ export const getColors = (sourceColors, sourceOpacity) => SLOT_ORDERED.reduce(({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, { colors: {}, opacity: {} })
|
}, { colors: {}, opacity: {} })
|
||||||
|
|
||||||
|
export const composePreset = (colors, radii, shadows, fonts) => {
|
||||||
|
return {
|
||||||
|
rules: {
|
||||||
|
...shadows.rules,
|
||||||
|
...colors.rules,
|
||||||
|
...radii.rules,
|
||||||
|
...fonts.rules
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
...shadows.theme,
|
||||||
|
...colors.theme,
|
||||||
|
...radii.theme,
|
||||||
|
...fonts.theme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const generatePreset = (input) => {
|
||||||
|
const colors = generateColors(input)
|
||||||
|
return composePreset(
|
||||||
|
colors,
|
||||||
|
generateRadii(input),
|
||||||
|
generateShadows(input, colors.theme.colors, colors.mod),
|
||||||
|
generateFonts(input)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getCssShadow = (input, usesDropShadow) => {
|
||||||
|
if (input.length === 0) {
|
||||||
|
return 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
return input
|
||||||
|
.filter(_ => usesDropShadow ? _.inset : _)
|
||||||
|
.map((shad) => [
|
||||||
|
shad.x,
|
||||||
|
shad.y,
|
||||||
|
shad.blur,
|
||||||
|
shad.spread
|
||||||
|
].map(_ => _ + 'px').concat([
|
||||||
|
getCssColor(shad.color, shad.alpha),
|
||||||
|
shad.inset ? 'inset' : ''
|
||||||
|
]).join(' ')).join(', ')
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCssShadowFilter = (input) => {
|
||||||
|
if (input.length === 0) {
|
||||||
|
return 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
return input
|
||||||
|
// drop-shadow doesn't support inset or spread
|
||||||
|
.filter((shad) => !shad.inset && Number(shad.spread) === 0)
|
||||||
|
.map((shad) => [
|
||||||
|
shad.x,
|
||||||
|
shad.y,
|
||||||
|
// drop-shadow's blur is twice as strong compared to box-shadow
|
||||||
|
shad.blur / 2
|
||||||
|
].map(_ => _ + 'px').concat([
|
||||||
|
getCssColor(shad.color, shad.alpha)
|
||||||
|
]).join(' '))
|
||||||
|
.map(_ => `drop-shadow(${_})`)
|
||||||
|
.join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const generateColors = (themeData) => {
|
||||||
|
const sourceColors = !themeData.themeEngineVersion
|
||||||
|
? colors2to3(themeData.colors || themeData)
|
||||||
|
: themeData.colors || themeData
|
||||||
|
|
||||||
|
const { colors, opacity } = getColors(sourceColors, themeData.opacity || {})
|
||||||
|
|
||||||
|
const htmlColors = Object.entries(colors)
|
||||||
|
.reduce((acc, [k, v]) => {
|
||||||
|
if (!v) return acc
|
||||||
|
acc.solid[k] = rgb2hex(v)
|
||||||
|
acc.complete[k] = typeof v.a === 'undefined' ? rgb2hex(v) : rgba2css(v)
|
||||||
|
return acc
|
||||||
|
}, { complete: {}, solid: {} })
|
||||||
|
return {
|
||||||
|
rules: {
|
||||||
|
colors: Object.entries(htmlColors.complete)
|
||||||
|
.filter(([k, v]) => v)
|
||||||
|
.map(([k, v]) => `--${k}: ${v}`)
|
||||||
|
.join(';')
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
colors: htmlColors.solid,
|
||||||
|
opacity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const generateRadii = (input) => {
|
||||||
|
let inputRadii = input.radii || {}
|
||||||
|
// v1 -> v2
|
||||||
|
if (typeof input.btnRadius !== 'undefined') {
|
||||||
|
inputRadii = Object
|
||||||
|
.entries(input)
|
||||||
|
.filter(([k, v]) => k.endsWith('Radius'))
|
||||||
|
.reduce((acc, e) => { acc[e[0].split('Radius')[0]] = e[1]; return acc }, {})
|
||||||
|
}
|
||||||
|
const radii = Object.entries(inputRadii).filter(([k, v]) => v).reduce((acc, [k, v]) => {
|
||||||
|
acc[k] = v
|
||||||
|
return acc
|
||||||
|
}, {
|
||||||
|
btn: 4,
|
||||||
|
input: 4,
|
||||||
|
checkbox: 2,
|
||||||
|
panel: 10,
|
||||||
|
avatar: 5,
|
||||||
|
avatarAlt: 50,
|
||||||
|
tooltip: 2,
|
||||||
|
attachment: 5,
|
||||||
|
chatMessage: inputRadii.panel
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
rules: {
|
||||||
|
radii: Object.entries(radii).filter(([k, v]) => v).map(([k, v]) => `--${k}Radius: ${v}px`).join(';')
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
radii
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const generateFonts = (input) => {
|
||||||
|
const fonts = Object.entries(input.fonts || {}).filter(([k, v]) => v).reduce((acc, [k, v]) => {
|
||||||
|
acc[k] = Object.entries(v).filter(([k, v]) => v).reduce((acc, [k, v]) => {
|
||||||
|
acc[k] = v
|
||||||
|
return acc
|
||||||
|
}, acc[k])
|
||||||
|
return acc
|
||||||
|
}, {
|
||||||
|
interface: {
|
||||||
|
family: 'sans-serif'
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
family: 'inherit'
|
||||||
|
},
|
||||||
|
post: {
|
||||||
|
family: 'inherit'
|
||||||
|
},
|
||||||
|
postCode: {
|
||||||
|
family: 'monospace'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
rules: {
|
||||||
|
fonts: Object
|
||||||
|
.entries(fonts)
|
||||||
|
.filter(([k, v]) => v)
|
||||||
|
.map(([k, v]) => `--${k}Font: ${v.family}`).join(';')
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
fonts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const border = (top, shadow) => ({
|
||||||
|
x: 0,
|
||||||
|
y: top ? 1 : -1,
|
||||||
|
blur: 0,
|
||||||
|
spread: 0,
|
||||||
|
color: shadow ? '#000000' : '#FFFFFF',
|
||||||
|
alpha: 0.2,
|
||||||
|
inset: true
|
||||||
|
})
|
||||||
|
const buttonInsetFakeBorders = [border(true, false), border(false, true)]
|
||||||
|
const inputInsetFakeBorders = [border(true, true), border(false, false)]
|
||||||
|
const hoverGlow = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
blur: 4,
|
||||||
|
spread: 0,
|
||||||
|
color: '--faint',
|
||||||
|
alpha: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_SHADOWS = {
|
||||||
|
panel: [{
|
||||||
|
x: 1,
|
||||||
|
y: 1,
|
||||||
|
blur: 4,
|
||||||
|
spread: 0,
|
||||||
|
color: '#000000',
|
||||||
|
alpha: 0.6
|
||||||
|
}],
|
||||||
|
topBar: [{
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
blur: 4,
|
||||||
|
spread: 0,
|
||||||
|
color: '#000000',
|
||||||
|
alpha: 0.6
|
||||||
|
}],
|
||||||
|
popup: [{
|
||||||
|
x: 2,
|
||||||
|
y: 2,
|
||||||
|
blur: 3,
|
||||||
|
spread: 0,
|
||||||
|
color: '#000000',
|
||||||
|
alpha: 0.5
|
||||||
|
}],
|
||||||
|
avatar: [{
|
||||||
|
x: 0,
|
||||||
|
y: 1,
|
||||||
|
blur: 8,
|
||||||
|
spread: 0,
|
||||||
|
color: '#000000',
|
||||||
|
alpha: 0.7
|
||||||
|
}],
|
||||||
|
avatarStatus: [],
|
||||||
|
panelHeader: [],
|
||||||
|
button: [{
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
blur: 2,
|
||||||
|
spread: 0,
|
||||||
|
color: '#000000',
|
||||||
|
alpha: 1
|
||||||
|
}, ...buttonInsetFakeBorders],
|
||||||
|
buttonHover: [hoverGlow, ...buttonInsetFakeBorders],
|
||||||
|
buttonPressed: [hoverGlow, ...inputInsetFakeBorders],
|
||||||
|
input: [...inputInsetFakeBorders, {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
blur: 2,
|
||||||
|
inset: true,
|
||||||
|
spread: 0,
|
||||||
|
color: '#000000',
|
||||||
|
alpha: 1
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
export const generateShadows = (input, colors) => {
|
||||||
|
// TODO this is a small hack for `mod` to work with shadows
|
||||||
|
// this is used to get the "context" of shadow, i.e. for `mod` properly depend on background color of element
|
||||||
|
const hackContextDict = {
|
||||||
|
button: 'btn',
|
||||||
|
panel: 'bg',
|
||||||
|
top: 'topBar',
|
||||||
|
popup: 'popover',
|
||||||
|
avatar: 'bg',
|
||||||
|
panelHeader: 'panel',
|
||||||
|
input: 'input'
|
||||||
|
}
|
||||||
|
|
||||||
|
const cleanInputShadows = Object.fromEntries(
|
||||||
|
Object.entries(input.shadows || {})
|
||||||
|
.map(([name, shadowSlot]) => [
|
||||||
|
name,
|
||||||
|
// defaulting color to black to avoid potential problems
|
||||||
|
shadowSlot.map(shadowDef => ({ color: '#000000', ...shadowDef }))
|
||||||
|
])
|
||||||
|
)
|
||||||
|
const inputShadows = cleanInputShadows && !input.themeEngineVersion
|
||||||
|
? shadows2to3(cleanInputShadows, input.opacity)
|
||||||
|
: cleanInputShadows || {}
|
||||||
|
const shadows = Object.entries({
|
||||||
|
...DEFAULT_SHADOWS,
|
||||||
|
...inputShadows
|
||||||
|
}).reduce((shadowsAcc, [slotName, shadowDefs]) => {
|
||||||
|
const slotFirstWord = slotName.replace(/[A-Z].*$/, '')
|
||||||
|
const colorSlotName = hackContextDict[slotFirstWord]
|
||||||
|
const isLightOnDark = relativeLuminance(convert(colors[colorSlotName]).rgb) < 0.5
|
||||||
|
const mod = isLightOnDark ? 1 : -1
|
||||||
|
const newShadow = shadowDefs.reduce((shadowAcc, def) => [
|
||||||
|
...shadowAcc,
|
||||||
|
{
|
||||||
|
...def,
|
||||||
|
color: rgb2hex(computeDynamicColor(
|
||||||
|
def.color,
|
||||||
|
(variableSlot) => convert(colors[variableSlot]).rgb,
|
||||||
|
mod
|
||||||
|
))
|
||||||
|
}
|
||||||
|
], [])
|
||||||
|
return { ...shadowsAcc, [slotName]: newShadow }
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
return {
|
||||||
|
rules: {
|
||||||
|
shadows: Object
|
||||||
|
.entries(shadows)
|
||||||
|
// TODO for v2.2: if shadow doesn't have non-inset shadows with spread > 0 - optionally
|
||||||
|
// convert all non-inset shadows into filter: drop-shadow() to boost performance
|
||||||
|
.map(([k, v]) => [
|
||||||
|
`--${k}Shadow: ${getCssShadow(v)}`,
|
||||||
|
`--${k}ShadowFilter: ${getCssShadowFilter(v)}`,
|
||||||
|
`--${k}ShadowInset: ${getCssShadow(v, true)}`
|
||||||
|
].join(';'))
|
||||||
|
.join(';')
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
shadows
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This handles compatibility issues when importing v2 theme's shadows to current format
|
||||||
|
*
|
||||||
|
* Back in v2 shadows allowed you to use dynamic colors however those used pure CSS3 variables
|
||||||
|
*/
|
||||||
|
export const shadows2to3 = (shadows, opacity) => {
|
||||||
|
return Object.entries(shadows).reduce((shadowsAcc, [slotName, shadowDefs]) => {
|
||||||
|
const isDynamic = ({ color = '#000000' }) => color.startsWith('--')
|
||||||
|
const getOpacity = ({ color }) => opacity[getOpacitySlot(color.substring(2).split(',')[0])]
|
||||||
|
const newShadow = shadowDefs.reduce((shadowAcc, def) => [
|
||||||
|
...shadowAcc,
|
||||||
|
{
|
||||||
|
...def,
|
||||||
|
alpha: isDynamic(def) ? getOpacity(def) || 1 : def.alpha
|
||||||
|
}
|
||||||
|
], [])
|
||||||
|
return { ...shadowsAcc, [slotName]: newShadow }
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const colors2to3 = (colors) => {
|
||||||
|
return Object.entries(colors).reduce((acc, [slotName, color]) => {
|
||||||
|
const btnPositions = ['', 'Panel', 'TopBar']
|
||||||
|
switch (slotName) {
|
||||||
|
case 'lightBg':
|
||||||
|
return { ...acc, highlight: color }
|
||||||
|
case 'btnText':
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
...btnPositions
|
||||||
|
.reduce(
|
||||||
|
(statePositionAcc, position) =>
|
||||||
|
({ ...statePositionAcc, ['btn' + position + 'Text']: color })
|
||||||
|
, {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return { ...acc, [slotName]: color }
|
||||||
|
}
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue