import React , { Component } from "react" ; import type { ForwardedRef , ComponentType , NamedExoticComponent , ComponentClass , } from "react" ; import type { Theme , ThemeKey , ThemeValue } from "../ThemeProvider/context" ; import { supportsCustomProperties } from "./utils" ; import { OStyleSheet } from "./stylesheet" ; const sheet = new OStyleSheet ( ) ; type SourceStyles = Record < string , string > ; type TemplateTheme = Record < string , ThemeValue > ; interface TranspiledStyles { __digest : string ; __template : ( theme : TemplateTheme ) => string ; } type ComponentThemeValue = ThemeValue | [ ThemeKey , ThemeValue ] ; export type ComponentTheme = Record < string , ComponentThemeValue > ; type VirtualComponentTheme = Record < string , ThemeValue > ; export type ThemeReducer = ( theme : Theme ) => VirtualComponentTheme ; export type Template = TranspiledStyles [ "__template" ] ; export type Styles = SourceStyles | TranspiledStyles ; export type Composable = ComponentType < any > | NamedExoticComponent < any > ; type WithThemeProps < P , R > = P & { theme : Theme ; composedRef : ForwardedRef < R > ; } ; export function withThemeFactory < ComposedRef , ComposedProps , C extends Composable > ( themeReducer , styles , Composed ) { type Props = WithThemeProps < ComposedProps , ComposedRef > ; const { __digest : digest , __template : template } = styles ; return class WithTheme extends Component < Props > { constructor ( props ) { super ( props ) ; if ( typeof template !== "function" ) return ; if ( sheet . has ( digest ) ) return ; sheet . inject ( digest , this . themedStyles ) ; } private get themedStyles ( ) : string { return ( template as Template ) ( this . templateTheme ) ; } private get templateTheme ( ) : TemplateTheme { const templateTheme = Object . fromEntries ( Object . entries ( this . componentTheme ) . map ( this . templateThemeEntry ) ) ; return new Proxy ( templateTheme , { get : ( target , key ) => { if ( key in target ) return target [ key as string ] ; throw new TypeError ( `Missing property '${ String ( key ) }' within ${ this . displayName } style module` ) ; } } ) ; } private get componentTheme ( ) : ComponentTheme { return themeReducer ( this . reducerTheme ) ; } private get reducerTheme ( ) : Theme { return new Proxy ( this . shallowCopyTheme , { get : ( target , key ) => { if ( key in target ) return [ key , target [ key as keyof Theme ] ] ; throw new TypeError ( `Missing property '${ String ( key ) }' within ${ this . displayName } theme module` ) ; } } ) ; } private get shallowCopyTheme ( ) : Theme { return Object . assign ( Object . create ( null ) , this . props . theme ) ; } private templateThemeEntry ( [ key , candidate ] ) { const isMappedValue = Array . isArray ( candidate ) ; const fallbackValue = isMappedValue ? candidate [ 1 ] : candidate ; const value = supportsCustomProperties ? isMappedValue ? `var(--${ digest }-${ key }, var(--${ candidate [ 0 ] }, ${ fallbackValue }))` : `var(--${ digest }-${ key }, ${ fallbackValue })` : fallbackValue ; return [ key , value ] ; } private get displayName ( ) : string { return Composed . displayName || Composed . name || "Component" ; } override render ( ) { const { composedRef : ref , theme , ... rest } = this . props ; const ComposedComponent = Composed ; return < ComposedComponent ref = { ref } { ... rest } />; } } ; }