|
1 | 1 | import type { Theme } from '../../theme' |
2 | 2 | import { escapeSelector } from '@unocss/core' |
| 3 | +import { transformThemeFn } from '@unocss/rule-utils' |
3 | 4 | import { globalKeywords } from '../mappings' |
4 | 5 | import { themeTracking } from '../track' |
5 | 6 | import { getThemeByKey } from '../utilities' |
6 | | -import { bracketTypeRe, numberRE, numberWithUnitRE, unitOnlyMap, unitOnlyRE } from './regex' |
| 7 | +import { bracketTypeRe, cssVarsRE, numberRE, numberWithUnitRE, unitOnlyMap, unitOnlyRE } from './regex' |
7 | 8 |
|
8 | 9 | // Not all, but covers most high frequency attributes |
9 | 10 | const cssProps = [ |
@@ -153,18 +154,28 @@ export function fraction(str: string) { |
153 | 154 | } |
154 | 155 | } |
155 | 156 |
|
156 | | -function processThemeVariable(name: string, key: keyof Theme, paths: string[], theme: Theme) { |
157 | | - const valOrObj = getThemeByKey(theme, key, paths) |
| 157 | +/** |
| 158 | + * Process a theme variable reference and retrieve its value and corresponding CSS variable key. |
| 159 | + * |
| 160 | + * @example theme => Theme object |
| 161 | + * @example themeKey => 'colors |
| 162 | + * @example themeKeyPaths => ['blue', '500'] |
| 163 | + * @example varPaths => 'colors.blue.500' |
| 164 | + * |
| 165 | + * @returns An object containing the resolved value from the theme and the corresponding CSS variable key. |
| 166 | + */ |
| 167 | +function processThemeVariable(theme: Theme, themeKey: string, themeKeyPaths: string[], varPaths: string) { |
| 168 | + const valOrObj = getThemeByKey(theme, themeKey as keyof Theme, themeKeyPaths) |
158 | 169 | const hasDefault = typeof valOrObj === 'object' && 'DEFAULT' in valOrObj |
159 | 170 |
|
160 | 171 | if (hasDefault) |
161 | | - paths.push('DEFAULT') |
| 172 | + themeKeyPaths.push('DEFAULT') |
162 | 173 |
|
163 | 174 | const val = hasDefault ? valOrObj.DEFAULT : valOrObj |
164 | | - const varKey = hasDefault && key !== 'spacing' ? `${name}.DEFAULT` : name |
| 175 | + const varKey = hasDefault && themeKey !== 'spacing' ? `${varPaths}.DEFAULT` : varPaths |
165 | 176 |
|
166 | 177 | if (val != null) |
167 | | - themeTracking(key, paths.length ? paths : undefined) |
| 178 | + themeTracking(themeKey, themeKeyPaths.length ? themeKeyPaths : undefined) |
168 | 179 |
|
169 | 180 | return { val, varKey } |
170 | 181 | } |
@@ -196,34 +207,34 @@ function bracketWithType(str: string, requiredType?: string, theme?: Theme) { |
196 | 207 | if (base === '=""') |
197 | 208 | return |
198 | 209 |
|
199 | | - if (base.startsWith('--')) { |
200 | | - const calcMatch = base.match(/^--([\w.-]+)\(([^)]+)\)$/) |
201 | | - if (calcMatch != null && theme) { |
202 | | - // Handle theme function with calculation: --theme.key(factor) |
203 | | - const [, name, factor] = calcMatch |
204 | | - const [key, ...paths] = name.split('.') as [keyof Theme, ...string[]] |
205 | | - const { val, varKey } = processThemeVariable(name, key, paths, theme) |
| 210 | + if (theme) { |
| 211 | + base = transformThemeFn(base, theme) |
| 212 | + } |
206 | 213 |
|
207 | | - if (val != null) |
208 | | - base = `calc(var(--${escapeSelector(varKey.replaceAll('.', '-'))}) * ${factor})` |
209 | | - } |
210 | | - else { |
211 | | - // Handle regular CSS variable: --name or --theme.key with optional default |
212 | | - const [name, defaultValue] = base.slice(2).split(',') |
213 | | - const suffix = defaultValue ? `, ${defaultValue}` : '' |
214 | | - const escapedName = escapeSelector(name) |
215 | | - |
216 | | - if (theme) { |
217 | | - const [key, ...paths] = name.split('.') as [keyof Theme, ...string[]] |
218 | | - const { val, varKey } = processThemeVariable(name, key, paths, theme) |
219 | | - base = val != null |
220 | | - ? `var(--${escapeSelector(varKey.replaceAll('.', '-'))}${suffix})` |
221 | | - : `var(--${escapedName}${suffix})` |
222 | | - } |
223 | | - else { |
224 | | - base = `var(--${escapedName}${suffix})` |
| 214 | + const matches = Array.from(base.matchAll(cssVarsRE)) |
| 215 | + for (const match of matches) { |
| 216 | + const [full, varPaths, _value] = match |
| 217 | + |
| 218 | + if (theme) { |
| 219 | + const [key, ...paths] = varPaths.split('.') |
| 220 | + const { val, varKey } = processThemeVariable(theme, key, paths, varPaths) |
| 221 | + |
| 222 | + if (val != null) { |
| 223 | + const cssVar = `--${varKey.replaceAll('.', '-')}` |
| 224 | + // use theme value with multiplier |
| 225 | + if (_value && !_value.startsWith(',')) { |
| 226 | + base = base.replace(full, `calc(var(${cssVar}) * ${_value.slice(1, -1)})`) |
| 227 | + } |
| 228 | + // default value |
| 229 | + else { |
| 230 | + const fallback = _value?.slice(1) |
| 231 | + base = base.replace(full, `var(${cssVar}${fallback ? `, ${fallback}` : ''})`) |
| 232 | + } |
| 233 | + continue |
225 | 234 | } |
226 | 235 | } |
| 236 | + |
| 237 | + base = base.replace(full, `var(${full})`) |
227 | 238 | } |
228 | 239 |
|
229 | 240 | let curly = 0 |
|
0 commit comments