Skip to content

Commit 8ca6061

Browse files
committed
- chore: add small comments
- chore: move Palette,ConfigValue and Config types into types.ts
1 parent c8ad5f6 commit 8ca6061

File tree

3 files changed

+39
-50
lines changed

3 files changed

+39
-50
lines changed

packages/ui/src/services/color/presetsToShare.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { Config, Palette } from './types'
12

23
export const presetToShare = {
34
blue: '#154ec1',
@@ -97,7 +98,7 @@ export const presetToShare = {
9798
skySoftDark: '#4b97de',
9899
skyLight: '#2379b6',
99100
skyDark: '#0a5c97',
100-
}
101+
} as Palette
101102

102103
export const lightNewTheme = {
103104
text: {
@@ -183,7 +184,7 @@ export const lightNewTheme = {
183184
info: 'sky20',
184185
brand: 'blue20',
185186
},
186-
}
187+
} as Config
187188

188189
export const darkNewTheme = {
189190
text: {
@@ -200,7 +201,7 @@ export const darkNewTheme = {
200201
brandHover: 'blueSoftLight',
201202
brandPressed: 'blueSoftDark',
202203
dangerHover: 'redSoftLight',
203-
dangerPressed: ['redPaleDark', 'redSoftDark'], // not all names from figma follow the same pattern
204+
dangerPressed: ['redPaleDark', 'redSoftDark'], // not all names from figma follow the same pattern - recommend to normalize them
204205
successHover: 'greenSoftLight',
205206
successPressed: 'greenSoftDark',
206207
warningHover: 'yellowSoftLight',
@@ -240,7 +241,7 @@ export const darkNewTheme = {
240241
dangerPressed: 'redSoft7',
241242
dangerHover: 'redSoft13',
242243
dangerAccentHover: 'redSoftLight',
243-
dangerAccentPressed: ['redPaleDark', 'redSoftDark'],
244+
dangerAccentPressed: ['redPaleDark', 'redSoftDark'], // not all names from figma follow the same pattern - recommend to normalize them
244245
successPressed: 'greenSoft7',
245246
successHover: 'greenSoft13',
246247
successAccentHover: 'greenSoftLight',
@@ -269,4 +270,4 @@ export const darkNewTheme = {
269270
info: 'skySoft20',
270271
brand: 'blueSoft20',
271272
},
272-
}
273+
} as Config

packages/ui/src/services/color/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,7 @@ export type ColorConfig = {
6464
},
6565
currentPresetName: string,
6666
}
67+
68+
export type Palette = Record<string, string>
69+
export type ConfigValue = string | string[] | Record<string, any>
70+
export type Config = Record<string, ConfigValue>

packages/ui/src/services/color/utils.ts

Lines changed: 29 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { camelCaseToKebabCase, kebabCaseToCamelCase } from '@/utils/text-case'
22

33
import { setHSLA, shiftHSLA, parseColorToRGB, parseColorToHSL, rgbToString, hslToString, colorToString, type RGBObject, type HSLObject } from '@/utils/color'
44

5+
import type { Config, Palette } from './types'
6+
57
export const isCSSVariable = (strColor: string): boolean => /var\(--.+\)/.test(strColor)
68
export const cssVariableName = (colorName: string) => `--va-${camelCaseToKebabCase(colorName)}`
79
export const normalizeColorName = (colorName: string) => kebabCaseToCamelCase(colorName)
@@ -122,63 +124,45 @@ export const isColorTransparent = (color: string) => {
122124

123125
export { isColor } from './../../utils/color'
124126

125-
type Palette = Record<string, string>
126-
127-
// Values in a theme config can be:
128-
// - a token string ("blue10")
129-
// - a prioritized list of token strings (["redPaleDark","redSoftDark"])
130-
// - a nested object (e.g., { brand: "...", danger: "..." })
131-
type ConfigValue = string | string[] | Record<string, any>
132-
type Config = Record<string, ConfigValue>
133-
134127
/**
135-
* resolveColors
128+
* Resolves color token references in config to actual color values from palette.
136129
*
137-
* Replaces token references in a config using a palette. Behavior:
138-
* - string: replace with palette[token] if present; otherwise keep as-is
139-
* - string[]: treat as fallback list; return the first palette hit;
140-
* if none hit, return the first element unchanged
141-
* - object: recurse into properties
130+
* Handles three types:
131+
* - String: "blue10" → palette["blue10"] or unchanged if not found
132+
* - Array: ["redPaleDark", "redSoftDark"] → first match from palette, or first item if none match
133+
* - Object: Recursively resolves nested properties
142134
*
143-
* Example:
144-
* dangerPressed: ["redPaleDark", "redSoftDark"]
145-
* -> resolves to palette["redPaleDark"] if it exists,
146-
* otherwise palette["redSoftDark"] if it exists,
147-
* otherwise "redPaleDark" (the first item) unchanged.
135+
* @example
136+
* resolveColors(
137+
* { danger: "red", fallback: ["custom", "red"] },
138+
* { red: "#ff0000" }
139+
* )
140+
* // Returns: { danger: "#ff0000", fallback: "#ff0000" }
148141
*/
149142
export function resolveColors (config: Config, palette: Palette): Config {
150-
const replaceValues = (obj: any): any => {
151-
// simple token string
152-
if (typeof obj === 'string') {
153-
return palette[obj] ?? obj
143+
const resolve = (value: any): any => {
144+
if (typeof value === 'string') {
145+
return palette[value] ?? value
154146
}
155147

156-
// arrays
157-
if (Array.isArray(obj)) {
158-
// If it's a list of strings, treat as fallback tokens
159-
if (obj.every((x) => typeof x === 'string')) {
160-
for (const token of obj) {
161-
if (palette[token]) { return palette[token] }
162-
}
163-
// no token matched: return the first entry unchanged (best-effort fallback)
164-
return obj[0]
148+
if (Array.isArray(value)) {
149+
// String array = fallback list (try each token until one matches)
150+
if (value.every((x) => typeof x === 'string')) {
151+
const match = value.find((token) => palette[token])
152+
return match ? palette[match] : value[0]
165153
}
166-
// Mixed array / non-strings: preserve previous behavior (map recursively)
167-
return obj.map(replaceValues)
154+
// Non-string array = map recursively
155+
return value.map(resolve)
168156
}
169157

170-
// objects (recurse)
171-
if (obj && typeof obj === 'object') {
172-
const result: Record<string, any> = {}
173-
for (const key of Object.keys(obj)) {
174-
result[key] = replaceValues(obj[key])
175-
}
176-
return result
158+
if (value && typeof value === 'object') {
159+
return Object.fromEntries(
160+
Object.entries(value).map(([key, val]) => [key, resolve(val)]),
161+
)
177162
}
178163

179-
// everything else (numbers, null, etc.)
180-
return obj
164+
return value
181165
}
182166

183-
return replaceValues(config)
167+
return resolve(config)
184168
}

0 commit comments

Comments
 (0)