import React , { createContext , useContext , useState , useCallback , useMemo , useEffect , useRef , } from 'react' ; import { EuiFlyout } from '@elastic/eui' ; interface Context { addContent : < P extends object = { [ key ] : any ; } > ( content : Content < P > ) => void ; removeContent : ( contentId : string ) => void ; closeFlyout : ( ) => void ; } interface Content < P extends object = { [ key ] : any ; } > { id : string ; Component : React . FunctionComponent < P > ; props ? : P ; flyoutProps ? : { [ key ] : any ; } ; cleanUpFunc ? : ( ) => void ; } const FlyoutMultiContentContext = createContext < Context | undefined > ( undefined ) ; const DEFAULT_FLYOUT_PROPS = { 'data-test-subj' : 'flyout' , size : 'm' as 'm' , maxWidth : 500 , } ; export const GlobalFlyoutProvider = ( { children } ) => { const [ showFlyout , setShowFlyout ] = useState ( false ) ; const [ activeContent , setActiveContent ] = useState < Content < any > | undefined > ( undefined ) ; const { id , Component , props , flyoutProps } = activeContent ?? { } ; const addContent = useCallback ( ( content ) => { setActiveContent ( ( prev ) => { if ( prev !== undefined ) { if ( prev . id !== content . id && prev . cleanUpFunc ) { prev . cleanUpFunc ( ) ; } } return content ; } ) ; setShowFlyout ( true ) ; } , [ ] ) ; const closeFlyout = useCallback ( ( ) => { setActiveContent ( undefined ) ; setShowFlyout ( false ) ; } , [ ] ) ; const removeContent = useCallback ( ( contentId ) => { if ( contentId === id ) { closeFlyout ( ) ; } } , [ id , closeFlyout ] ) ; const mergedFlyoutProps = useMemo ( ( ) => { return { ... DEFAULT_FLYOUT_PROPS , onClose : closeFlyout , ... flyoutProps , } ; } , [ flyoutProps , closeFlyout ] ) ; const context = { addContent , removeContent , closeFlyout , } ; const ContentFlyout = showFlyout && Component !== undefined ? Component : null ; return ( < FlyoutMultiContentContext . Provider value = { context } > < > { children } { ContentFlyout && ( < EuiFlyout { ... mergedFlyoutProps } > < ContentFlyout { ... props } / > < /EuiFlyout>)} < /> < /FlyoutMultiContentContext.Provider>); } ; export const useGlobalFlyout = ( ) => { const ctx = useContext ( FlyoutMultiContentContext ) ; if ( ctx === undefined ) { throw new Error ( 'useGlobalFlyout must be used within a <GlobalFlyoutProvider />' ) ; } const isMounted = useRef ( false ) ; /**
     * A component can add one or multiple content to the flyout
     * during its lifecycle. When it unmounts, we will remove
     * all those content added to the flyout.
     */ const contents = useRef < Set < string > | undefined > ( undefined ) ; const { removeContent , addContent : addContentToContext } = ctx ; useEffect ( ( ) => { isMounted . current = true ; return ( ) => { isMounted . current = false ; } ; } , [ ] ) ; const getContents = useCallback ( ( ) => { if ( contents . current === undefined ) { contents . current = new Set ( ) ; } return contents . current ; } , [ ] ) ; const addContent = useCallback ( ( content ) => { getContents ( ) . add ( content . id ) ; return addContentToContext ( content ) ; } , [ getContents , addContentToContext ] ) ; useEffect ( ( ) => { return ( ) => { if ( ! isMounted . current ) { Array . from ( getContents ( ) ) . forEach ( removeContent ) ; } } ; } , [ removeContent , getContents ] ) ; return { ... ctx , addContent } ; } ;