2024-02-19 18:48:49 +02:00
import { convert , brightness } from 'chromatism'
2024-03-04 19:53:45 +02:00
import { alphaBlend , getTextColor , relativeLuminance } from '../color_convert/color_convert.js'
2024-02-19 18:48:49 +02:00
2024-02-21 22:18:56 +02:00
export const process = ( text , functions , { findColor , findShadow } , { dynamicVars , staticVars } ) => {
2024-02-19 18:48:49 +02:00
const { funcName , argsString } = /\$(?<funcName>\w+)\((?<argsString>[#a-zA-Z0-9-,.'"\s]*)\)/ . exec ( text ) . groups
2024-10-02 23:59:56 +03:00
const args = argsString . split ( / /g ) . map ( a => a . trim ( ) )
2024-02-19 18:48:49 +02:00
const func = functions [ funcName ]
if ( args . length < func . argsNeeded ) {
throw new Error ( ` $ ${ funcName } requires at least ${ func . argsNeeded } arguments, but ${ args . length } were provided ` )
}
2024-02-21 22:18:56 +02:00
return func . exec ( args , { findColor , findShadow } , { dynamicVars , staticVars } )
2024-02-19 18:48:49 +02:00
}
export const colorFunctions = {
alpha : {
argsNeeded : 2 ,
2024-11-07 18:34:59 +02:00
documentation : 'Changes alpha value of the color only to be used for CSS variables' ,
args : [
'color: source color used' ,
'amount: alpha value'
] ,
2024-02-21 22:18:56 +02:00
exec : ( args , { findColor } , { dynamicVars , staticVars } ) => {
2024-02-19 18:48:49 +02:00
const [ color , amountArg ] = args
2024-02-21 22:18:56 +02:00
const colorArg = convert ( findColor ( color , { dynamicVars , staticVars } ) ) . rgb
2024-02-19 18:48:49 +02:00
const amount = Number ( amountArg )
return { ... colorArg , a : amount }
}
} ,
2024-10-30 16:01:06 +02:00
brightness : {
argsNeeded : 2 ,
2024-11-07 18:34:59 +02:00
document : 'Changes brightness/lightness of color in HSL colorspace' ,
args : [
'color: source color used' ,
'amount: lightness value'
] ,
2024-10-30 16:01:06 +02:00
exec : ( args , { findColor } , { dynamicVars , staticVars } ) => {
const [ color , amountArg ] = args
const colorArg = convert ( findColor ( color , { dynamicVars , staticVars } ) ) . hsl
colorArg . l += Number ( amountArg )
return { ... convert ( colorArg ) . rgb }
}
} ,
2024-03-04 19:53:45 +02:00
textColor : {
argsNeeded : 2 ,
2024-11-07 18:34:59 +02:00
documentation : 'Get text color with adequate contrast for given background and intended text color. Same function is used internally' ,
args : [
'background: color of backdrop where text will be shown' ,
'foreground: intended text color' ,
` [preserve]: (optional) intended color preservation:
'preserve' - try to preserve the color
'no-preserve' - if can ' t get adequate color - fall back to black or white
'no-auto' - don ' t do anything ( useless as a color function ) `
] ,
2024-03-04 19:53:45 +02:00
exec : ( args , { findColor } , { dynamicVars , staticVars } ) => {
const [ backgroundArg , foregroundArg , preserve = 'preserve' ] = args
const background = convert ( findColor ( backgroundArg , { dynamicVars , staticVars } ) ) . rgb
const foreground = convert ( findColor ( foregroundArg , { dynamicVars , staticVars } ) ) . rgb
return getTextColor ( background , foreground , preserve === 'preserve' )
}
} ,
2024-02-19 18:48:49 +02:00
blend : {
argsNeeded : 3 ,
2024-11-07 18:34:59 +02:00
documentation : 'Alpha blending between two colors' ,
args : [
'background: bottom layer color' ,
'amount: opacity of top layer' ,
'foreground: upper layer color'
] ,
2024-02-21 22:18:56 +02:00
exec : ( args , { findColor } , { dynamicVars , staticVars } ) => {
2024-02-19 18:48:49 +02:00
const [ backgroundArg , amountArg , foregroundArg ] = args
2024-02-21 22:18:56 +02:00
const background = convert ( findColor ( backgroundArg , { dynamicVars , staticVars } ) ) . rgb
const foreground = convert ( findColor ( foregroundArg , { dynamicVars , staticVars } ) ) . rgb
2024-02-19 18:48:49 +02:00
const amount = Number ( amountArg )
return alphaBlend ( background , amount , foreground )
}
} ,
2025-01-07 17:50:49 +02:00
boost : {
argsNeeded : 2 ,
documentation : 'If given color is dark makes it darker, if color is light - makes it lighter' ,
args : [
'color: source color' ,
'amount: how much darken/brighten the color'
] ,
exec : ( args , { findColor } , { dynamicVars , staticVars } ) => {
const [ colorArg , amountArg ] = args
const color = convert ( findColor ( colorArg , { dynamicVars , staticVars } ) ) . rgb
const amount = Number ( amountArg )
const isLight = relativeLuminance ( color ) < 0.5
const mod = isLight ? - 1 : 1
return brightness ( amount * mod , color ) . rgb
}
} ,
2024-02-19 18:48:49 +02:00
mod : {
argsNeeded : 2 ,
2025-01-07 17:50:49 +02:00
documentation : 'Old function that increases or decreases brightness depending if background color is dark or light. Advised against using it as it might give unexpected results.' ,
2024-11-07 18:34:59 +02:00
args : [
'color: source color' ,
'amount: how much darken/brighten the color'
] ,
2024-02-21 22:18:56 +02:00
exec : ( args , { findColor } , { dynamicVars , staticVars } ) => {
2024-02-19 18:48:49 +02:00
const [ colorArg , amountArg ] = args
2024-02-21 22:18:56 +02:00
const color = convert ( findColor ( colorArg , { dynamicVars , staticVars } ) ) . rgb
2024-02-19 18:48:49 +02:00
const amount = Number ( amountArg )
const effectiveBackground = dynamicVars . lowerLevelBackground
const isLightOnDark = relativeLuminance ( convert ( effectiveBackground ) . rgb ) < 0.5
const mod = isLightOnDark ? 1 : - 1
return brightness ( amount * mod , color ) . rgb
}
}
}
export const shadowFunctions = {
borderSide : {
argsNeeded : 3 ,
2024-11-07 18:34:59 +02:00
documentation : 'Simulate a border on a side with a shadow, best works on inset border' ,
args : [
'color: border color' ,
'side: string indicating on which side border should be, takes either one word or two words joined by dash (i.e. "left" or "bottom-right")' ,
2025-01-07 18:28:24 +02:00
'width: border width (thickness)' ,
2024-11-07 18:34:59 +02:00
'[alpha]: (Optional) border opacity, defaults to 1 (fully opaque)' ,
'[inset]: (Optional) whether border should be on the inside or outside, defaults to inside'
] ,
2024-02-21 22:18:56 +02:00
exec : ( args , { findColor } ) => {
2024-02-19 18:48:49 +02:00
const [ color , side , alpha = '1' , widthArg = '1' , inset = 'inset' ] = args
const width = Number ( widthArg )
const isInset = inset === 'inset'
const targetShadow = {
x : 0 ,
y : 0 ,
blur : 0 ,
spread : 0 ,
color ,
alpha : Number ( alpha ) ,
inset : isInset
}
side . split ( '-' ) . forEach ( ( position ) => {
switch ( position ) {
case 'left' :
targetShadow . x = width * ( inset ? 1 : - 1 )
break
case 'right' :
targetShadow . x = - 1 * width * ( inset ? 1 : - 1 )
break
case 'top' :
targetShadow . y = width * ( inset ? 1 : - 1 )
break
case 'bottom' :
targetShadow . y = - 1 * width * ( inset ? 1 : - 1 )
break
}
} )
2024-02-21 22:18:56 +02:00
return [ targetShadow ]
2024-02-19 18:48:49 +02:00
}
}
}