import './app_container.scss' ; import { Observable } from 'rxjs' ; import React , { Fragment , FunctionComponent , useLayoutEffect , useRef , useState , MutableRefObject , } from 'react' ; import { EuiLoadingElastic } from '@elastic/eui' ; import { i18n } from '@kbn/i18n' ; import type { MountPoint } from '../../types' ; import { CoreTheme } from '../../theme' ; import { AppLeaveHandler , AppStatus , AppUnmount , Mounter } from '../types' ; import { AppNotFound } from './app_not_found_screen' ; import { ScopedHistory } from '../scoped_history' ; import { APP_WRAPPER_CLASS } from '../../../utils' ; interface Props { /** Path application is mounted on without the global basePath */ appPath : string ; appId : string ; mounter ? : Mounter ; theme$ : Observable < CoreTheme > ; appStatus : AppStatus ; setAppLeaveHandler : ( appId : string , handler : AppLeaveHandler ) => void ; setAppActionMenu : ( appId : string , mount : MountPoint | undefined ) => void ; createScopedHistory : ( appUrl : string ) => ScopedHistory ; setIsMounting : ( isMounting : boolean ) => void ; } export const AppContainer = ( { mounter , appId , appPath , setAppLeaveHandler , setAppActionMenu , createScopedHistory , appStatus , setIsMounting , theme$ , } ) => { const [ showSpinner , setShowSpinner ] = useState ( true ) ; const [ appNotFound , setAppNotFound ] = useState ( false ) ; const elementRef = useRef < HTMLDivElement > ( null ) ; const unmountRef = useRef < AppUnmount > ( null ) ; useLayoutEffect ( ( ) => { const unmount = ( ) => { if ( unmountRef . current ) { unmountRef . current ( ) ; unmountRef . current = null ; } } ; if ( ! mounter || appStatus !== AppStatus . accessible ) { return setAppNotFound ( true ) ; } setAppNotFound ( false ) ; setIsMounting ( true ) ; if ( mounter . unmountBeforeMounting ) { unmount ( ) ; } const mount = async ( ) => { setShowSpinner ( true ) ; try { unmountRef . current = ( await mounter . mount ( { appBasePath : mounter . appBasePath , history : createScopedHistory ( appPath ) , element : elementRef . current ! , theme$ , onAppLeave : ( handler ) => setAppLeaveHandler ( appId , handler ) , setHeaderActionMenu : ( menuMount ) => setAppActionMenu ( appId , menuMount ) , } ) ) || null ; } catch ( e ) { console . error ( e ) ; } finally { if ( elementRef . current ) { setShowSpinner ( false ) ; setIsMounting ( false ) ; } } } ; mount ( ) ; return unmount ; } , [ appId , appStatus , mounter , createScopedHistory , setAppLeaveHandler , setAppActionMenu , appPath , setIsMounting , theme$ , ] ) ; return ( < Fragment > { appNotFound && < AppNotFound />} { showSpinner && ! appNotFound && ( < EuiLoadingElastic className = "appContainer__loading" aria - label = { i18n . translate ( 'core.application.appContainer.loadingAriaLabel' , { defaultMessage : 'Loading application' , } ) } size = "xxl" / > ) } < div className = { APP_WRAPPER_CLASS } key = { appId } ref = { elementRef } aria - busy = { showSpinner } /> < /Fragment>); } ;