import { Dimensions, Platform } from 'react-native' import { Orientation, Platform as UniwindPlatform, StyleDependency, UNIWIND_PLATFORM_VARIABLES, UNIWIND_THEME_VARIABLES } from '../../common/consts' import { UniwindListener } from '../listener' import type { ComponentState, GenerateStyleSheetsCallback, RNStyle, Style, StyleSheets, ThemeName, UniwindContextType, Var, Vars } from '../types' import { parseBoxShadow, parseFontVariant, parseTextShadowMutation, parseTransformsMutation, resolveGradient } from './parsers' import { UniwindRuntime } from './runtime' type StylesResult = { styles: RNStyle dependencies: Array dependencySum: number } const emptyState: StylesResult = { styles: {}, dependencies: [], dependencySum: 0 } class UniwindStoreBuilder { runtime = UniwindRuntime vars = {} as Record private stylesheet = {} as StyleSheets private cache = {} as Record> getStyles( className: string | undefined, componentProps: Record | undefined, state: ComponentState | undefined, uniwindContext: UniwindContextType, ): StylesResult { if (className === undefined || className === '') { return emptyState } const isScopedTheme = uniwindContext.scopedTheme !== null const cacheKey = `${className}${state?.isDisabled ?? false}${state?.isFocused ?? false}${state?.isPressed ?? false}${isScopedTheme}${ uniwindContext.rtl ?? '' }` const cache = this.cache[uniwindContext.scopedTheme ?? this.runtime.currentThemeName] if (!cache) { return emptyState } if (cache.has(cacheKey)) { return cache.get(cacheKey)! } const result = this.resolveStyles(className, componentProps, state, uniwindContext) // Don't cache styles that depend on data attributes if (!result.hasDataAttributes) { cache.set(cacheKey, result) UniwindListener.subscribe( () => cache.delete(cacheKey), result.dependencies, { once: true }, ) } return result } reinit = (generateStyleSheetCallback: GenerateStyleSheetsCallback, themes: Array) => { const config = generateStyleSheetCallback(this.runtime) const { scopedVars, stylesheet, vars } = config const platform = this.getCurrentPlatform() const commonPlatform = platform.includes('tv') ? UniwindPlatform.TV : UniwindPlatform.Native const commonPlatformVars = scopedVars[`${UNIWIND_PLATFORM_VARIABLES}${commonPlatform}`] const platformVars = scopedVars[`${UNIWIND_PLATFORM_VARIABLES}${platform}`] if (commonPlatformVars) { Object.assign(vars, commonPlatformVars) } if (platformVars) { Object.assign(vars, platformVars) } this.stylesheet = stylesheet this.vars = Object.fromEntries(themes.map(theme => { const clonedVars = Object.create(vars) as Vars const themeVars = scopedVars[`${UNIWIND_THEME_VARIABLES}${theme}`] if (themeVars) { Object.assign(clonedVars, themeVars) } return [theme, clonedVars] })) this.cache = Object.fromEntries(themes.map(theme => [theme, new Map()])) if (__DEV__) { UniwindListener.notifyAll() } } private resolveStyles( classNames: string, componentProps: Record | undefined, state: ComponentState | undefined, uniwindContext: UniwindContextType, ) { const resultGetters = {} as Record const theme = uniwindContext.scopedTheme ?? this.runtime.currentThemeName // At this point we're sure that theme is correct let vars = this.vars[theme]! const originalVars = vars let hasDataAttributes = false const dependencies = new Set() let dependencySum = 0 const bestBreakpoints = new Map() const isScopedTheme = uniwindContext.scopedTheme !== null for (const className of classNames.split(' ')) { if (!(className in this.stylesheet)) { continue } for (const style of this.stylesheet[className] as Array