forked from AkkomaGang/akkoma-fe
opacity handling
This commit is contained in:
parent
c3cd66335f
commit
f16ec75c70
4 changed files with 149 additions and 126 deletions
|
@ -15,7 +15,7 @@ import {
|
||||||
import {
|
import {
|
||||||
CURRENT_VERSION,
|
CURRENT_VERSION,
|
||||||
SLOT_INHERITANCE,
|
SLOT_INHERITANCE,
|
||||||
DEFAULT_OPACITY,
|
OPACITIES,
|
||||||
getLayers
|
getLayers
|
||||||
} from '../../services/theme_data/theme_data.service.js'
|
} from '../../services/theme_data/theme_data.service.js'
|
||||||
import ColorInput from '../color_input/color_input.vue'
|
import ColorInput from '../color_input/color_input.vue'
|
||||||
|
@ -74,8 +74,8 @@ export default {
|
||||||
.map(key => [key, ''])
|
.map(key => [key, ''])
|
||||||
.reduce((acc, [key, val]) => ({ ...acc, [ key + 'ColorLocal' ]: val }), {}),
|
.reduce((acc, [key, val]) => ({ ...acc, [ key + 'ColorLocal' ]: val }), {}),
|
||||||
|
|
||||||
...Object.keys(DEFAULT_OPACITY)
|
...Object.keys(OPACITIES)
|
||||||
.map(key => [key, undefined])
|
.map(key => console.log(key) || [key, ''])
|
||||||
.reduce((acc, [key, val]) => ({ ...acc, [ key + 'OpacityLocal' ]: val }), {}),
|
.reduce((acc, [key, val]) => ({ ...acc, [ key + 'OpacityLocal' ]: val }), {}),
|
||||||
|
|
||||||
shadowSelected: undefined,
|
shadowSelected: undefined,
|
||||||
|
@ -115,8 +115,8 @@ export default {
|
||||||
.reduce((acc, [key, val]) => ({ ...acc, [ key ]: val }), {})
|
.reduce((acc, [key, val]) => ({ ...acc, [ key ]: val }), {})
|
||||||
},
|
},
|
||||||
currentOpacity () {
|
currentOpacity () {
|
||||||
return Object.keys(DEFAULT_OPACITY)
|
return Object.keys(OPACITIES)
|
||||||
.map(key => [key, this[key + 'OpacityLocal']])
|
.map(key => console.log(key) || [key, this[key + 'OpacityLocal']])
|
||||||
.reduce((acc, [key, val]) => ({ ...acc, [ key ]: val }), {})
|
.reduce((acc, [key, val]) => ({ ...acc, [ key ]: val }), {})
|
||||||
},
|
},
|
||||||
currentRadii () {
|
currentRadii () {
|
||||||
|
|
|
@ -159,7 +159,7 @@ export const hex2rgb = (hex) => {
|
||||||
* @returns {Object} result
|
* @returns {Object} result
|
||||||
*/
|
*/
|
||||||
export const mixrgb = (a, b) => {
|
export const mixrgb = (a, b) => {
|
||||||
return Object.keys(a).reduce((acc, k) => {
|
return 'rgb'.split('').reduce((acc, k) => {
|
||||||
acc[k] = (a[k] + b[k]) / 2
|
acc[k] = (a[k] + b[k]) / 2
|
||||||
return acc
|
return acc
|
||||||
}, {})
|
}, {})
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { times } from 'lodash'
|
import { times } from 'lodash'
|
||||||
import { convert } from 'chromatism'
|
import { convert } from 'chromatism'
|
||||||
import { rgb2hex, hex2rgb, rgba2css, getCssColor } from '../color_convert/color_convert.js'
|
import { rgb2hex, hex2rgb, rgba2css, getCssColor } from '../color_convert/color_convert.js'
|
||||||
import { getColors, DEFAULT_OPACITY } from '../theme_data/theme_data.service.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
|
// 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 that aren't just colors, so user can pick from a few different distinct
|
||||||
|
@ -115,76 +115,12 @@ const getCssShadowFilter = (input) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generateColors = (themeData) => {
|
export const generateColors = (themeData) => {
|
||||||
const rawOpacity = Object.assign({ ...DEFAULT_OPACITY }, Object.entries(themeData.opacity || {}).reduce((acc, [k, v]) => {
|
const sourceColors = themeData.colors || themeData
|
||||||
if (typeof v !== 'undefined') {
|
|
||||||
acc[k] = v
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
}, {}))
|
|
||||||
|
|
||||||
const inputColors = themeData.colors || themeData
|
|
||||||
|
|
||||||
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]) => {
|
|
||||||
if (typeof v === 'object') {
|
|
||||||
acc[k] = v
|
|
||||||
} else {
|
|
||||||
let value = v
|
|
||||||
if (v === 'transparent') {
|
|
||||||
// TODO: hack to keep rest of the code from complaining
|
|
||||||
value = '#FF00FF'
|
|
||||||
}
|
|
||||||
if (!value || value.startsWith('--')) {
|
|
||||||
acc[k] = value
|
|
||||||
} else {
|
|
||||||
acc[k] = hex2rgb(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
}, {})
|
|
||||||
|
|
||||||
const isLightOnDark = convert(sourceColors.bg).hsl.l < convert(sourceColors.text).hsl.l
|
const isLightOnDark = convert(sourceColors.bg).hsl.l < convert(sourceColors.text).hsl.l
|
||||||
const mod = isLightOnDark ? 1 : -1
|
const mod = isLightOnDark ? 1 : -1
|
||||||
|
|
||||||
const colors = getColors(sourceColors, opacity, mod)
|
const { colors, opacity } = getColors(sourceColors, themeData.opacity || {}, mod)
|
||||||
|
|
||||||
// Inheriting opacities
|
|
||||||
Object.entries(opacity).forEach(([ k, v ]) => {
|
|
||||||
if (typeof v === 'undefined') return
|
|
||||||
if (k === 'alert') {
|
|
||||||
colors.alertError.a = v
|
|
||||||
colors.alertWarning.a = v
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (k === 'faint') {
|
|
||||||
colors['faintLink'].a = v
|
|
||||||
colors['panelFaint'].a = v
|
|
||||||
colors['lightBgFaintText'].a = v
|
|
||||||
colors['lightBgFaintLink'].a = v
|
|
||||||
}
|
|
||||||
if (k === 'bg') {
|
|
||||||
colors['lightBg'].a = v
|
|
||||||
}
|
|
||||||
if (k === 'badge') {
|
|
||||||
colors['badgeNotification'].a = v
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (colors[k]) {
|
|
||||||
colors[k].a = v
|
|
||||||
} else {
|
|
||||||
console.error('Wrong key ' + k)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const htmlColors = Object.entries(colors)
|
const htmlColors = Object.entries(colors)
|
||||||
.reduce((acc, [k, v]) => {
|
.reduce((acc, [k, v]) => {
|
||||||
|
|
|
@ -26,24 +26,17 @@ export const LAYERS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_OPACITY = {
|
export const DEFAULT_OPACITY = {
|
||||||
panel: 1,
|
|
||||||
btn: 1,
|
|
||||||
border: 1,
|
|
||||||
bg: 1,
|
|
||||||
badge: 1,
|
|
||||||
text: 1,
|
|
||||||
alert: 0.5,
|
alert: 0.5,
|
||||||
input: 0.5,
|
input: 0.5,
|
||||||
faint: 0.5,
|
faint: 0.5,
|
||||||
underlay: 0.15,
|
underlay: 0.15
|
||||||
poll: 1,
|
|
||||||
topBar: 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SLOT_INHERITANCE = {
|
export const SLOT_INHERITANCE = {
|
||||||
bg: {
|
bg: {
|
||||||
depends: [],
|
depends: [],
|
||||||
priority: 1
|
priority: 1,
|
||||||
|
opacity: 'bg'
|
||||||
},
|
},
|
||||||
fg: {
|
fg: {
|
||||||
depends: [],
|
depends: [],
|
||||||
|
@ -53,7 +46,10 @@ export const SLOT_INHERITANCE = {
|
||||||
depends: [],
|
depends: [],
|
||||||
priority: 1
|
priority: 1
|
||||||
},
|
},
|
||||||
underlay: '#000000',
|
underlay: {
|
||||||
|
default: '#000000',
|
||||||
|
opacity: 'underlay'
|
||||||
|
},
|
||||||
link: {
|
link: {
|
||||||
depends: ['accent'],
|
depends: ['accent'],
|
||||||
priority: 1
|
priority: 1
|
||||||
|
@ -62,8 +58,14 @@ export const SLOT_INHERITANCE = {
|
||||||
depends: ['link'],
|
depends: ['link'],
|
||||||
priority: 1
|
priority: 1
|
||||||
},
|
},
|
||||||
faint: '--text',
|
faint: {
|
||||||
faintLink: '--link',
|
depends: ['text'],
|
||||||
|
opacity: 'faint'
|
||||||
|
},
|
||||||
|
faintLink: {
|
||||||
|
depends: ['link'],
|
||||||
|
opacity: 'faint'
|
||||||
|
},
|
||||||
|
|
||||||
cBlue: '#0000ff',
|
cBlue: '#0000ff',
|
||||||
cRed: '#FF0000',
|
cRed: '#FF0000',
|
||||||
|
@ -158,11 +160,13 @@ export const SLOT_INHERITANCE = {
|
||||||
|
|
||||||
border: {
|
border: {
|
||||||
depends: ['fg'],
|
depends: ['fg'],
|
||||||
|
opacity: 'border',
|
||||||
color: (mod, fg) => brightness(2 * mod, fg).rgb
|
color: (mod, fg) => brightness(2 * mod, fg).rgb
|
||||||
},
|
},
|
||||||
|
|
||||||
poll: {
|
poll: {
|
||||||
depends: ['accent', 'bg'],
|
depends: ['accent', 'bg'],
|
||||||
|
copacity: 'poll',
|
||||||
color: (mod, accent, bg) => alphaBlend(accent, 0.4, bg)
|
color: (mod, accent, bg) => alphaBlend(accent, 0.4, bg)
|
||||||
},
|
},
|
||||||
pollText: {
|
pollText: {
|
||||||
|
@ -173,6 +177,7 @@ export const SLOT_INHERITANCE = {
|
||||||
|
|
||||||
icon: {
|
icon: {
|
||||||
depends: ['bg', 'text'],
|
depends: ['bg', 'text'],
|
||||||
|
inheritsOpacity: false,
|
||||||
color: (mod, bg, text) => mixrgb(bg, text)
|
color: (mod, bg, text) => mixrgb(bg, text)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -189,7 +194,10 @@ export const SLOT_INHERITANCE = {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Panel header
|
// Panel header
|
||||||
panel: '--fg',
|
panel: {
|
||||||
|
depends: ['fg'],
|
||||||
|
opacity: 'panel'
|
||||||
|
},
|
||||||
panelText: {
|
panelText: {
|
||||||
depends: ['fgText'],
|
depends: ['fgText'],
|
||||||
layer: 'panel',
|
layer: 'panel',
|
||||||
|
@ -198,6 +206,7 @@ export const SLOT_INHERITANCE = {
|
||||||
panelFaint: {
|
panelFaint: {
|
||||||
depends: ['fgText'],
|
depends: ['fgText'],
|
||||||
layer: 'panel',
|
layer: 'panel',
|
||||||
|
opacity: 'faint',
|
||||||
textColor: true
|
textColor: true
|
||||||
},
|
},
|
||||||
panelLink: {
|
panelLink: {
|
||||||
|
@ -233,7 +242,10 @@ export const SLOT_INHERITANCE = {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Buttons
|
// Buttons
|
||||||
btn: '--fg',
|
btn: {
|
||||||
|
depends: ['fg'],
|
||||||
|
opacity: 'btn'
|
||||||
|
},
|
||||||
btnText: {
|
btnText: {
|
||||||
depends: ['fgText'],
|
depends: ['fgText'],
|
||||||
layer: 'btn',
|
layer: 'btn',
|
||||||
|
@ -325,7 +337,10 @@ export const SLOT_INHERITANCE = {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Input fields
|
// Input fields
|
||||||
input: '--fg',
|
input: {
|
||||||
|
depends: ['fg'],
|
||||||
|
opacity: 'input'
|
||||||
|
},
|
||||||
inputText: {
|
inputText: {
|
||||||
depends: ['text'],
|
depends: ['text'],
|
||||||
layer: 'input',
|
layer: 'input',
|
||||||
|
@ -344,7 +359,10 @@ export const SLOT_INHERITANCE = {
|
||||||
textColor: true
|
textColor: true
|
||||||
},
|
},
|
||||||
|
|
||||||
alertError: '--cRed',
|
alertError: {
|
||||||
|
depends: ['cRed'],
|
||||||
|
opacity: 'alert'
|
||||||
|
},
|
||||||
alertErrorText: {
|
alertErrorText: {
|
||||||
depends: ['text'],
|
depends: ['text'],
|
||||||
layer: 'alert',
|
layer: 'alert',
|
||||||
|
@ -358,7 +376,10 @@ export const SLOT_INHERITANCE = {
|
||||||
textColor: true
|
textColor: true
|
||||||
},
|
},
|
||||||
|
|
||||||
alertWarning: '--cOrange',
|
alertWarning: {
|
||||||
|
depends: ['cOrange'],
|
||||||
|
opacity: 'alert'
|
||||||
|
},
|
||||||
alertWarningText: {
|
alertWarningText: {
|
||||||
depends: ['text'],
|
depends: ['text'],
|
||||||
layer: 'alert',
|
layer: 'alert',
|
||||||
|
@ -465,78 +486,144 @@ export const topoSort = (
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getOpacitySlot = (
|
||||||
|
v,
|
||||||
|
inheritance = SLOT_INHERITANCE,
|
||||||
|
getDeps = getDependencies
|
||||||
|
) => {
|
||||||
|
if (v.opacity === null) return
|
||||||
|
if (v.opacity) return v.opacity
|
||||||
|
const findInheritedOpacity = (val) => {
|
||||||
|
const depSlot = val.depends[0]
|
||||||
|
if (depSlot === undefined) return
|
||||||
|
const dependency = getDeps(depSlot, inheritance)[0]
|
||||||
|
if (dependency === undefined) return
|
||||||
|
if (dependency.opacity || dependency === null) {
|
||||||
|
return dependency.opacity
|
||||||
|
} else if (dependency.depends) {
|
||||||
|
return findInheritedOpacity(dependency)
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (v.depends) {
|
||||||
|
return findInheritedOpacity(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const SLOT_ORDERED = topoSort(
|
export const SLOT_ORDERED = topoSort(
|
||||||
Object.entries(SLOT_INHERITANCE)
|
Object.entries(SLOT_INHERITANCE)
|
||||||
.sort(([aK, aV], [bK, bV]) => ((aV && aV.priority) || 0) - ((bV && bV.priority) || 0))
|
.sort(([aK, aV], [bK, bV]) => ((aV && aV.priority) || 0) - ((bV && bV.priority) || 0))
|
||||||
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {})
|
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {})
|
||||||
)
|
)
|
||||||
|
|
||||||
console.log(SLOT_ORDERED)
|
export const SLOTS_OPACITIES_DICT = Object.entries(SLOT_INHERITANCE).reduce((acc, [k, v]) => {
|
||||||
|
const opacity = getOpacitySlot(v, SLOT_INHERITANCE, getDependencies)
|
||||||
|
if (opacity) {
|
||||||
|
return { ...acc, [k]: opacity }
|
||||||
|
} else {
|
||||||
|
return acc
|
||||||
|
}
|
||||||
|
}, {})
|
||||||
|
|
||||||
export const getColors = (sourceColors, sourceOpacity, mod) => SLOT_ORDERED.reduce((acc, key) => {
|
export const OPACITIES = Object.entries(SLOT_INHERITANCE).reduce((acc, [k, v]) => {
|
||||||
|
const opacity = getOpacitySlot(v, SLOT_INHERITANCE, getDependencies)
|
||||||
|
if (opacity) {
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
[opacity]: {
|
||||||
|
defaultValue: DEFAULT_OPACITY[opacity] || 1,
|
||||||
|
affectedSlots: [...((acc[opacity] && acc[opacity].affectedSlots) || []), k]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return acc
|
||||||
|
}
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
export const getColors = (sourceColors, sourceOpacity, mod) => SLOT_ORDERED.reduce(({ colors, opacity }, key) => {
|
||||||
const value = SLOT_INHERITANCE[key]
|
const value = SLOT_INHERITANCE[key]
|
||||||
|
const isObject = typeof value === 'object'
|
||||||
|
const isString = typeof value === 'string'
|
||||||
const sourceColor = sourceColors[key]
|
const sourceColor = sourceColors[key]
|
||||||
|
let outputColor = null
|
||||||
if (sourceColor) {
|
if (sourceColor) {
|
||||||
|
// Color is defined in source color
|
||||||
let targetColor = sourceColor
|
let targetColor = sourceColor
|
||||||
if (typeof sourceColor === 'string' && sourceColor.startsWith('--')) {
|
if (targetColor === 'transparent') {
|
||||||
|
targetColor = {
|
||||||
|
// TODO: try to use alpha-blended background here
|
||||||
|
...convert('#FF00FF').rgb,
|
||||||
|
a: 0
|
||||||
|
}
|
||||||
|
} else if (typeof sourceColor === 'string' && sourceColor.startsWith('--')) {
|
||||||
|
// Color references other color
|
||||||
const [variable, modifier] = sourceColor.split(/,/g).map(str => str.trim())
|
const [variable, modifier] = sourceColor.split(/,/g).map(str => str.trim())
|
||||||
const variableSlot = variable.substring(2)
|
const variableSlot = variable.substring(2)
|
||||||
targetColor = acc[variableSlot] || sourceColors[variableSlot]
|
targetColor = colors[variableSlot] || sourceColors[variableSlot]
|
||||||
if (modifier) {
|
if (modifier) {
|
||||||
console.log(targetColor, acc, variableSlot)
|
|
||||||
targetColor = brightness(Number.parseFloat(modifier) * mod, targetColor).rgb
|
targetColor = brightness(Number.parseFloat(modifier) * mod, targetColor).rgb
|
||||||
}
|
}
|
||||||
console.log(targetColor, acc, variableSlot)
|
} else if (typeof sourceColor === 'string' && sourceColor.startsWith('#')) {
|
||||||
|
targetColor = convert(targetColor).rgb
|
||||||
}
|
}
|
||||||
return { ...acc, [key]: { ...targetColor } }
|
outputColor = { ...targetColor }
|
||||||
} else if (typeof value === 'string' && value.startsWith('#')) {
|
} else if (isString && value.startsWith('#')) {
|
||||||
return { ...acc, [key]: convert(value).rgb }
|
// slot: '#000000' shorthand
|
||||||
|
outputColor = convert(value).rgb
|
||||||
|
} else if (isObject && value.default) {
|
||||||
|
// same as above except in object form
|
||||||
|
outputColor = convert(value.default).rgb
|
||||||
} else {
|
} else {
|
||||||
const isObject = typeof value === 'object'
|
// calculate color
|
||||||
const defaultColorFunc = (mod, dep) => ({ ...dep })
|
const defaultColorFunc = (mod, dep) => ({ ...dep })
|
||||||
const deps = getDependencies(key, SLOT_INHERITANCE)
|
const deps = getDependencies(key, SLOT_INHERITANCE)
|
||||||
const colorFunc = (isObject && value.color) || defaultColorFunc
|
const colorFunc = (isObject && value.color) || defaultColorFunc
|
||||||
|
|
||||||
if (value.textColor) {
|
if (value.textColor) {
|
||||||
|
// textColor case
|
||||||
const bg = alphaBlendLayers(
|
const bg = alphaBlendLayers(
|
||||||
{ ...acc[deps[0]] },
|
{ ...colors[deps[0]] },
|
||||||
getLayers(
|
getLayers(
|
||||||
value.layer,
|
value.layer,
|
||||||
value.variant || value.layer,
|
value.variant || value.layer,
|
||||||
acc,
|
colors,
|
||||||
sourceOpacity
|
opacity
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if (value.textColor === 'bw') {
|
if (value.textColor === 'bw') {
|
||||||
return {
|
outputColor = contrastRatio(bg).rgb
|
||||||
...acc,
|
|
||||||
[key]: contrastRatio(bg).rgb
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let color = { ...acc[deps[0]] }
|
let color = { ...colors[deps[0]] }
|
||||||
if (value.color) {
|
if (value.color) {
|
||||||
const isLightOnDark = convert(bg).hsl.l < convert(color).hsl.l
|
const isLightOnDark = convert(bg).hsl.l < convert(color).hsl.l
|
||||||
const mod = isLightOnDark ? 1 : -1
|
const mod = isLightOnDark ? 1 : -1
|
||||||
color = value.color(mod, ...deps.map((dep) => ({ ...acc[dep] })))
|
color = value.color(mod, ...deps.map((dep) => ({ ...colors[dep] })))
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
outputColor = getTextColor(
|
||||||
...acc,
|
|
||||||
[key]: getTextColor(
|
|
||||||
bg,
|
bg,
|
||||||
{ ...color },
|
{ ...color },
|
||||||
value.textColor === 'preserve'
|
value.textColor === 'preserve'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return {
|
// background color case
|
||||||
...acc,
|
outputColor = colorFunc(
|
||||||
[key]: colorFunc(
|
|
||||||
mod,
|
mod,
|
||||||
...deps.map((dep) => ({ ...acc[dep] }))
|
...deps.map((dep) => ({ ...colors[dep] }))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!outputColor) {
|
||||||
|
throw new Error('Couldn\'t generate color for ' + key)
|
||||||
}
|
}
|
||||||
}, {})
|
const opacitySlot = SLOTS_OPACITIES_DICT[key]
|
||||||
|
if (opacitySlot && outputColor.a === undefined) {
|
||||||
|
outputColor.a = sourceOpacity[opacitySlot] || OPACITIES[opacitySlot].defaultValue || 1
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
colors: { ...colors, [key]: outputColor },
|
||||||
|
opacity: { ...opacity, [opacitySlot]: outputColor.a }
|
||||||
|
}
|
||||||
|
}, { colors: {}, opacity: {} })
|
||||||
|
|
Loading…
Reference in a new issue