UNPKG

@pandacss/studio

Version:

The automated token documentation for Panda CSS

133 lines (111 loc) 4.13 kB
import { TokenDictionary } from '@pandacss/token-dictionary' import type { Token, TokenExtensions } from '@pandacss/token-dictionary' import { useState, useMemo, useDeferredValue } from 'react' import * as context from './panda-context' interface Color { isConditional?: boolean isReference?: boolean name: string originalValue: string path: string[] } export type ColorToken = Token & Color & TokenExtensions const UNCATEGORIZED_ID = 'uncategorized' as const const groupByColorPalette = (colors: ColorToken[], filterMethod?: (token: ColorToken) => boolean) => { const values = colors.filter((color) => !color.isConditional && !color.extensions.isVirtual) return values.reduce( (acc, color) => { if (!filterMethod?.(color)) return acc const colorPalette = color.extensions.colorPalette || UNCATEGORIZED_ID if (!(colorPalette in acc)) { acc[colorPalette] = [] } const exists = (acc[colorPalette] as any[]).find((tok) => tok.name === color.name) if (!exists) acc[colorPalette].push(color) return acc }, {} as Record<string, ColorToken[]>, ) } const getSemanticTokens = (allTokens: Token[], filterMethod?: (token: ColorToken) => boolean) => { const semanticTokens = allTokens.filter( (token) => token.type === 'color' && token.isConditional && !token.extensions?.isVirtual, ) as ColorToken[] return semanticTokens .reduce((acc, nxt) => { if (!filterMethod) { acc.push(nxt) } else { const rawQualified = semanticTokens.filter(filterMethod) if (filterMethod(nxt) || rawQualified.some((tok) => tok.name === nxt.name)) { acc.push(nxt) } } return acc }, [] as ColorToken[]) .reduce<Record<string, ColorToken>>( (acc, nxt) => ({ ...acc, [nxt.extensions?.prop]: { ...acc[nxt.extensions?.prop], // @ts-ignore [nxt.extensions.condition]: { value: nxt.value, isReference: nxt.isReference }, extensions: nxt.extensions, }, }), {}, ) } export const useColorDocs = (theme?: string) => { const [filterQuery, setFilterQuery] = useState('') const deferredQuery = useDeferredValue(filterQuery) // Memoize token data based on theme to ensure reactivity const { colors, allTokens } = useMemo(() => { // Get tokens based on provided theme (filtered to show only relevant tokens when theme is active) const colors = context.getThemeRelevantTokens('colors', theme) // Get all tokens for the active theme const activeTheme = context.getActiveTheme(theme) const themeTokens = new TokenDictionary(activeTheme).init() const allTokens = themeTokens.allTokens return { colors, allTokens } }, [theme]) // Memoize processed data based on theme and filter query (deferred for performance) const processedData = useMemo(() => { const filterMethod = (token: ColorToken) => { return [ ...token.path, token.originalValue, token.description, token.value, token.name, token.extensions?.var, token.extensions?.prop, ...Object.values(token.extensions?.conditions || {}), ] .filter(Boolean) .some((prop) => prop.includes(deferredQuery)) } const colorsInCategories = groupByColorPalette(colors as ColorToken[], filterMethod) const uncategorizedColors = colorsInCategories[UNCATEGORIZED_ID] const categorizedColors = Object.entries<any[]>(colorsInCategories).filter( ([category]) => category !== UNCATEGORIZED_ID, ) const semanticTokens = Object.entries<Record<string, any>>(getSemanticTokens(allTokens, filterMethod)) as [ string, Record<string, ColorToken>, ][] const hasResults = !!categorizedColors.length || !!uncategorizedColors?.length || !!Object.values(semanticTokens).length return { uncategorizedColors, categorizedColors, semanticTokens, hasResults, } }, [colors, allTokens, deferredQuery]) return { filterQuery, setFilterQuery, ...processedData, } }