import { getStyleRules } from '@tamagui/helpers' ; import { isWeb } from './constants/platform' ; import { Variable , createVariable } from './createVariable' ; import { createTamaguiProvider } from './helpers/createTamaguiProvider' ; import { configureMedia } from './hooks/useMedia' ; import { CreateTamaguiConfig , GenericTamaguiConfig , MediaQueryKey , TamaguiInternalConfig , TamaguiProviderProps , } from './types' ; export type CreateTamaguiProps = TamaguiProviderProps & Omit < GenericTamaguiConfig , 'themes' > & { themes : { [ key ] : { [ key ] : string | number | Variable ; } ; } ; mediaQueryDefaultActive ? : MediaQueryKey [ ] ; } ; let conf ; export const getHasConfigured = ( ) => ! ! conf ; export const getTamagui = ( ) => conf ! ; export const getTokens = ( ) => conf ! . tokensParsed ; export const getThemes = ( ) => conf ! . themes ; type ConfigListener = ( conf : TamaguiInternalConfig ) => void ; const configListeners = new Set < ConfigListener > ( ) ; export const onConfiguredOnce = ( cb ) => { if ( conf ) { cb ( conf ) ; } else { configListeners . add ( cb ) ; } } ; const PREFIX = `theme--` ; export function createTamagui < Conf extends CreateTamaguiProps > ( config ) { if ( config [ 'parsed' ] ) { return config as any ; } if ( conf ) { console . warn ( 'Called createTamagui twice! Should never do so' ) ; } configureMedia ( { queries : config . media as any , defaultActive : config . mediaQueryDefaultActive , } ) ; const themeConfig = ( ( ) => { let cssRules = [ ] ; const varsByValue = new Map < string , Variable > ( ) ; if ( isWeb ) { let tokenRule = `:root {` ; const addVar = ( v ) => { varsByValue [ v . val ] = v ; const rule = `--${ v . name }:${ typeof v . val === 'number' ? `${ v . val }px` : v . val };` ; tokenRule += rule ; } ; for ( const key in config . tokens ) { for ( const skey in config . tokens [ key ] ) { const val = config . tokens [ key ] [ skey ] ; if ( key === 'font' ) { for ( const fkey in val ) { if ( fkey === 'family' ) { addVar ( val [ fkey ] ) ; } else { for ( const fskey in val [ fkey ] ) { addVar ( val [ fkey ] [ fskey ] ) ; } } } } else { addVar ( val ) ; } } } cssRules . push ( `${ tokenRule }\n}` ) ; } for ( const themeName in config . themes ) { const theme = { ... config . themes [ themeName ] } ; config . themes [ themeName ] = theme ; let vars = '' ; for ( const themeKey in theme ) { if ( isWeb ) { const val = theme [ themeKey ] ; const varName = val instanceof Variable ? val . name : varsByValue [ val ] ?. name ; vars += `--${ themeKey }:${ varName ? `var(--${ varName });` : `${ val }` };` ; } ensureThemeVariable ( theme , themeKey ) ; } if ( isWeb ) { const [ themeClassName , parentName ] = themeName . includes ( '-' ) ? themeName . split ( '-' ) : [ themeName , '' ] ; const oppositeName = parentName === 'dark' ? 'light' : 'dark' ; const cssParentSel = parentName ? `.${ PREFIX }${ parentName }` : '' ; const cssChildSel = `.${ PREFIX }${ themeClassName }` ; const cssSel = `${ cssParentSel } ${ cssChildSel }` ; const cssRule = `${ cssSel }, .${ PREFIX }${ oppositeName } ${ cssSel } {\n${ vars }\n}` ; cssRules . push ( cssRule ) ; } } varsByValue . clear ( ) ; Object . freeze ( cssRules ) ; return { cssRules , css : cssRules . join ( '\n' ) , } ; } ) ( ) ; const tokensParsed = parseTokens ( config . tokens ) ; const themeParsed = { } ; const firstKey = Object . keys ( config . themes ) [ 0 ] ; for ( const key in config . themes [ firstKey ] ) { const val = config . themes [ firstKey ] [ key ] . val ; themeParsed [ `$${ key }` ] = new Variable ( { val , name : key } ) ; } const next = { shorthands : { } , ... config , Provider : createTamaguiProvider ( { defaultTheme : 'light' , ... config , } ) , themeConfig , themeParsed , tokensParsed , parsed : true , getCSS ( ) { return `${ themeConfig . css }\n${ [ ... getStyleRules ( ) ] . join ( '\n' ) }` ; } , } ; conf = next ; if ( configListeners . size ) { configListeners . forEach ( ( cb ) => cb ( next ) ) ; configListeners . clear ( ) ; } return next ; } export function getThemeParentClassName ( themeName ? ) { return `theme-parent ${ themeName ? `${ PREFIX }${ themeName }` : '' }` ; } const parseTokens = ( tokens ) => { const res = { } ; for ( const key in tokens ) { res [ key ] = { } ; if ( key === 'font' ) { for ( const family in tokens . font ) { const obj = { } ; res . font [ `$${ family }` ] = obj ; for ( const attrKey in tokens . font [ family ] ) { const attr = tokens . font [ family ] [ attrKey ] ; if ( attrKey === 'family' ) { obj [ attrKey ] = attr ; continue ; } obj [ attrKey ] = Object . keys ( attr ) . reduce ( ( acc , cur ) => { acc [ `$${ cur }` ] = attr [ cur ] ; return acc ; } , { } ) ; } } continue ; } for ( const skey in tokens [ key ] ) { res [ key ] [ `$${ skey }` ] = tokens [ key ] [ skey ] ; } } return res ; } ; function ensureThemeVariable ( theme , key ) { const val = theme [ key ] ; if ( ! ( val instanceof Variable ) ) { theme [ key ] = createVariable ( { name : key , val , } ) ; } else { if ( val . name !== key ) { theme [ key ] = createVariable ( { name : key , val : val . val , } ) ; } } }