import * as React from "react" ; import { useContext , useEffect , useState , useMemo } from "react" ; import { Smart } from "./Smart" ; const SmartOptionsDefaults = { isolated : false , } ; export function setDefaults ( defaults ) { Object . assign ( SmartOptionsDefaults , defaults ) ; } type Constructor < T > = { new ( ... args ) : T ; } ; type SmartConstructor < S , U > = { new ( ... args ) : Smart < S , U > ; } ; export interface INewSmartOptions { factory ? < S , U > ( targetType , config ) : Smart < S , U > ; /**
     * Isolated means that it will not react to state changes.
     */ isolated ? : boolean ; } export const newSmart = < S , U , T extends Smart < S , U > > ( targetType , config ? , options ? ) : [ T , React . ComponentType < any > ] => { if ( ! targetType ) throw new Error ( `You have tried to create a Smart with a falsy value: "${ targetType }"` ) ; options = Object . assign ( { } , options , SmartOptionsDefaults ) ; const model = useMemo ( ( ) => { return createSmartModel < S , U , T > ( options , targetType , config ) ; } , [ ] ) ; const Provider = useMemo ( ( ) => { return ( { children } ) => { return React . createElement ( ( targetType as any ) . getContext ( ) . Provider , { value : model , children , } ) ; } ; } , [ ] ) ; if ( options . isolated ) { } else { reactToSmartStateChange ( model ) ; } useEffect ( ( ) => { model . init ( ) ; return function cleanup ( ) { model . destroy ( ) ; } ; } , [ ] ) ; return [ model , Provider ] ; } ; type Returnable < T > = ( ... args : any [ ] ) => T ; function createSmartModel < S , U , T extends Smart < S , U > > ( options , targetType , config ) { let model ; if ( options ?. factory ) { model = options . factory ( targetType , config ) ; } else { model = new targetType ( ) ; } model . setConfig ( config ) ; return model ; } param targetType param config param options export function smart < T extends Smart < S , U > , S , U > ( targetType , config ? , options ? ) { return function ( Component ) { const Container = function ( props ) { const [ api , Provider ] = newSmart ( targetType , config , options ) ; return React . createElement ( Provider , { children : React . createElement ( Component , props ) , } ) ; } ; Container . displayName = `SmartComponent(${ getDisplayName ( Component ) })` ; return Container ; } ; } export type UseSmartOptions = { /**
     * This option is used when you only want to use the api and not react on state changes.
     */ isolated ? : boolean ; } ; export const useSmart = < T extends Smart > ( modelClass , options ? ) : T => { const model = useContext < T > ( ( modelClass as any ) . getContext ( ) ) ; if ( options ?. isolated ) { } else { reactToSmartStateChange ( model ) ; } return model ; } ; function reactToSmartStateChange ( model ) { const [ , updateState ] = React . useState ( { } ) ; const forceUpdate = React . useCallback ( ( ) => updateState ( { } ) , [ ] ) ; useMemo ( ( ) => { model . subscribe ( forceUpdate ) ; return null ; } , [ ] ) ; useEffect ( ( ) => { return ( ) => { model . unsubscribe ( forceUpdate ) ; } ; } , [ ] ) ; } function getDisplayName ( WrappedComponent ) { return WrappedComponent . displayName || WrappedComponent . name || "Component" ; }