|
1 | 1 | import { isCssUnit, isString, PandaError } from '@pandacss/shared' |
2 | 2 | import type { TokenDataTypes } from '@pandacss/types' |
| 3 | +import picomatch from 'picomatch' |
3 | 4 | import { P, match } from 'ts-pattern' |
4 | 5 | import type { TokenTransformer } from './dictionary' |
5 | 6 | import { isCompositeBorder, isCompositeGradient, isCompositeShadow } from './is-composite' |
@@ -238,58 +239,56 @@ export const addColorPalette: TokenTransformer = { |
238 | 239 | // If disabled, don't add colorPalette extensions |
239 | 240 | if (!enabled) return {} |
240 | 241 |
|
241 | | - let tokenPathClone = [...token.path] |
242 | | - tokenPathClone.pop() |
243 | | - tokenPathClone.shift() |
244 | | - |
245 | | - if (tokenPathClone.length === 0) { |
246 | | - const newPath = [...token.path] |
247 | | - newPath.shift() |
248 | | - tokenPathClone = newPath |
| 242 | + // Extract color path (remove 'colors' prefix and last segment) |
| 243 | + // ['colors', 'blue', '500'] -> ['blue'] |
| 244 | + // ['colors', 'button', 'light', 'accent', 'secondary'] -> ['button', 'light', 'accent'] |
| 245 | + // ['colors', 'primary'] -> ['primary'] (handle flat tokens) |
| 246 | + let colorPath = token.path.slice(1, -1) |
| 247 | + |
| 248 | + // If no nested segments, use the path without the 'colors' prefix |
| 249 | + if (colorPath.length === 0) { |
| 250 | + colorPath = token.path.slice(1) |
| 251 | + if (colorPath.length === 0) { |
| 252 | + return {} |
| 253 | + } |
249 | 254 | } |
250 | 255 |
|
251 | | - if (tokenPathClone.length === 0) { |
252 | | - return {} |
| 256 | + // Convert path segments to dot-notation string for pattern matching |
| 257 | + const colorPathString = colorPath.join('.') |
| 258 | + |
| 259 | + // Check include/exclude filters using picomatch (supports glob patterns) |
| 260 | + // Exclude takes precedence over include |
| 261 | + if (exclude?.length) { |
| 262 | + const excludeMatchers = exclude.map((pattern) => picomatch(pattern)) |
| 263 | + if (excludeMatchers.some((matcher) => matcher(colorPathString))) { |
| 264 | + return {} |
| 265 | + } |
253 | 266 | } |
254 | 267 |
|
255 | | - // Check include/exclude filters |
256 | | - const colorName = token.path[1] // e.g., 'blue' from ['colors', 'blue', '500'] |
257 | | - if (include && !include.includes(colorName)) return {} |
258 | | - if (exclude && exclude.includes(colorName)) return {} |
| 268 | + if (include?.length) { |
| 269 | + const includeMatchers = include.map((pattern) => picomatch(pattern)) |
| 270 | + if (!includeMatchers.some((matcher) => matcher(colorPathString))) { |
| 271 | + return {} |
| 272 | + } |
| 273 | + } |
259 | 274 |
|
260 | 275 | /** |
261 | | - * If this is the nested color palette: |
262 | | - * ```json |
263 | | - * { |
264 | | - * "colors": { |
265 | | - * "button": { |
266 | | - * "light": { |
267 | | - * "accent": { |
268 | | - * "secondary": { |
269 | | - * value: 'blue', |
270 | | - * }, |
271 | | - * }, |
272 | | - * }, |
273 | | - * }, |
274 | | - * }, |
275 | | - * }, |
276 | | - * ``` |
| 276 | + * Generate all possible color palette roots from the color path. |
| 277 | + * |
| 278 | + * For ['button', 'light', 'accent']: |
| 279 | + * - ['button'] |
| 280 | + * - ['button', 'light'] |
| 281 | + * - ['button', 'light', 'accent'] |
277 | 282 | * |
278 | | - * The `colorPaletteRoots` will be `['button', 'button.light', 'button.light.accent']`. |
279 | | - * It holds all the possible values you can pass to the css `colorPalette` property. |
280 | | - * It's used by the `addVirtualPalette` middleware to build the virtual `colorPalette` token for each color pattern root. |
| 283 | + * These represent all possible values you can pass to the css `colorPalette` property. |
281 | 284 | */ |
282 | | - const colorPaletteRoots = tokenPathClone.reduce( |
283 | | - (acc, _, i, arr) => { |
284 | | - const next = arr.slice(0, i + 1) |
285 | | - acc.push(next) |
286 | | - return acc |
287 | | - }, |
288 | | - [] as Array<string[]>, |
289 | | - ) |
290 | | - |
291 | | - const colorPaletteRoot = tokenPathClone[0] |
292 | | - const colorPalette = dict.formatTokenName(tokenPathClone) |
| 285 | + const colorPaletteRoots: string[][] = [] |
| 286 | + for (let i = 0; i < colorPath.length; i++) { |
| 287 | + colorPaletteRoots.push(colorPath.slice(0, i + 1)) |
| 288 | + } |
| 289 | + |
| 290 | + const colorPaletteRoot = colorPath[0] |
| 291 | + const colorPalette = dict.formatTokenName(colorPath) |
293 | 292 |
|
294 | 293 | /** |
295 | 294 | * If this is the nested color palette: |
@@ -343,16 +342,15 @@ export const addColorPalette: TokenTransformer = { |
343 | 342 | * })} |
344 | 343 | * /> |
345 | 344 | */ |
346 | | - const colorPaletteTokenKeys = token.path |
347 | | - // Remove everything before colorPalette root and the root itself |
348 | | - .slice(token.path.indexOf(colorPaletteRoot) + 1) |
349 | | - .reduce( |
350 | | - (acc, _, i, arr) => { |
351 | | - acc.push(arr.slice(i)) |
352 | | - return acc |
353 | | - }, |
354 | | - [] as Array<string[]>, |
355 | | - ) |
| 345 | + // Remove everything before colorPalette root and the root itself |
| 346 | + const startIndex = token.path.indexOf(colorPaletteRoot) + 1 |
| 347 | + const remainingPath = token.path.slice(startIndex) |
| 348 | + const colorPaletteTokenKeys: string[][] = [] |
| 349 | + |
| 350 | + // Generate all suffixes of the remaining path |
| 351 | + for (let i = 0; i < remainingPath.length; i++) { |
| 352 | + colorPaletteTokenKeys.push(remainingPath.slice(i)) |
| 353 | + } |
356 | 354 |
|
357 | 355 | // https://github.com/chakra-ui/panda/issues/1421 |
358 | 356 | if (colorPaletteTokenKeys.length === 0) { |
|
0 commit comments