import React , { useEffect , useState } from 'react' ; import { EmbeddableInput , IEmbeddable } from './i_embeddable' ; import { EmbeddableRoot } from './embeddable_root' ; import { EmbeddableFactory } from './embeddable_factory' ; import { ErrorEmbeddable , isErrorEmbeddable } from './error_embeddable' ; /**
 * This type is a publicly exposed props of  EmbeddableRenderer * Union is used to validate that or factory or embeddable is passed in, but it can't be both simultaneously
 * In case when embeddable is passed in, input is optional, because there is already an input inside of embeddable object
 * In case when factory is used, then input is required, because it will be used as initial input to create an embeddable object
  export type EmbeddableRendererProps < I extends EmbeddableInput > = EmbeddableRendererPropsWithEmbeddable < I > | EmbeddableRendererWithFactory < I > ; interface EmbeddableRendererPropsWithEmbeddable < I extends EmbeddableInput > { input ? : I ; onInputUpdated ? : ( newInput : I ) => void ; embeddable : IEmbeddable < I > ; } function isWithEmbeddable < I extends EmbeddableInput > ( props ) { return 'embeddable' in props ; } interface EmbeddableRendererWithFactory < I extends EmbeddableInput > { input : I ; onInputUpdated ? : ( newInput : I ) => void ; factory : EmbeddableFactory < I > ; } function isWithFactory < I extends EmbeddableInput > ( props ) { return 'factory' in props ; } remarks public param props -  EmbeddableRendererProps export const EmbeddableRenderer = < I extends EmbeddableInput > ( props ) => { const { input , onInputUpdated } = props ; const [ embeddable , setEmbeddable ] = useState < IEmbeddable < I > | ErrorEmbeddable | undefined > ( isWithEmbeddable ( props ) ? props . embeddable : undefined ) ; const [ loading , setLoading ] = useState < boolean > ( ! isWithEmbeddable ( props ) ) ; const [ error , setError ] = useState < string | undefined > ( ) ; const latestInput = React . useRef ( props . input ) ; useEffect ( ( ) => { latestInput . current = input ; } , [ input ] ) ; const factoryFromProps = isWithFactory ( props ) ? props . factory : undefined ; const embeddableFromProps = isWithEmbeddable ( props ) ? props . embeddable : undefined ; useEffect ( ( ) => { let canceled = false ; if ( embeddableFromProps ) { setEmbeddable ( embeddableFromProps ) ; return ; } let createdEmbeddableRef ; if ( factoryFromProps ) { setEmbeddable ( undefined ) ; setLoading ( true ) ; factoryFromProps . create ( latestInput . current ! ) . then ( ( createdEmbeddable ) => { if ( canceled ) { if ( createdEmbeddable ) { createdEmbeddable . destroy ( ) ; } } else { createdEmbeddableRef = createdEmbeddable ; setEmbeddable ( createdEmbeddable ) ; } } ) . catch ( ( err ) => { if ( canceled ) return ; setError ( err ?. message ) ; } ) . finally ( ( ) => { if ( canceled ) return ; setLoading ( false ) ; } ) ; } return ( ) => { canceled = true ; if ( createdEmbeddableRef ) { createdEmbeddableRef . destroy ( ) ; } } ; } , [ factoryFromProps , embeddableFromProps ] ) ; useEffect ( ( ) => { if ( ! embeddable ) return ; if ( isErrorEmbeddable ( embeddable ) ) return ; if ( ! onInputUpdated ) return ; const sub = embeddable . getInput$ ( ) . subscribe ( ( newInput ) => { onInputUpdated ( newInput ) ; } ) ; return ( ) => { sub . unsubscribe ( ) ; } ; } , [ embeddable , onInputUpdated ] ) ; return < EmbeddableRoot embeddable = { embeddable } loading = { loading } error = { error } input = { input } / > ; } ;