import React from 'react' ; import ReactDOM from 'react-dom' ; import { I18nProvider } from '@osd/i18n/react' ; import { RefreshInterval , TimeRange , Query , Filter } from 'src/plugins/data/public' ; import { CoreStart } from 'src/core/public' ; import { Start as InspectorStartContract } from 'src/plugins/inspector/public' ; import uuid from 'uuid' ; import { UiActionsStart } from '../../ui_actions_plugin' ; import { Container , ContainerInput , EmbeddableInput , ViewMode , EmbeddableFactory , IEmbeddable , EmbeddableStart , PanelState , } from '../../embeddable_plugin' ; import { DASHBOARD_CONTAINER_TYPE } from './dashboard_constants' ; import { createPanelState } from './panel' ; import { DashboardPanelState } from './types' ; import { DashboardViewport } from './viewport/dashboard_viewport' ; import { OpenSearchDashboardsContextProvider , OpenSearchDashboardsReactContext , OpenSearchDashboardsReactContextValue , } from '../../../../opensearch_dashboards_react/public' ; import { PLACEHOLDER_EMBEDDABLE } from './placeholder' ; import { PanelPlacementMethod , IPanelPlacementArgs } from './panel/dashboard_panel_placement' ; import { EmbeddableStateTransfer , EmbeddableOutput } from '../../../../embeddable/public' ; export interface DashboardContainerInput extends ContainerInput { viewMode : ViewMode ; filters : Filter [ ] ; query : Query ; timeRange : TimeRange ; refreshConfig ? : RefreshInterval ; expandedPanelId ? : string ; useMargins : boolean ; title : string ; description ? : string ; isEmbeddedExternally ? : boolean ; isFullScreenMode : boolean ; panels : { [ panelId ] : DashboardPanelState < EmbeddableInput & { [ k ] : unknown ; } > ; } ; isEmptyState ? : boolean ; } interface IndexSignature { [ key ] : unknown ; } export interface InheritedChildInput extends IndexSignature { filters : Filter [ ] ; query : Query ; timeRange : TimeRange ; refreshConfig ? : RefreshInterval ; viewMode : ViewMode ; hidePanelTitles ? : boolean ; id : string ; } export interface DashboardContainerOptions { application : CoreStart [ 'application' ] ; overlays : CoreStart [ 'overlays' ] ; notifications : CoreStart [ 'notifications' ] ; embeddable : EmbeddableStart ; inspector : InspectorStartContract ; SavedObjectFinder : React . ComponentType < any > ; ExitFullScreenButton : React . ComponentType < any > ; uiActions : UiActionsStart ; } export type DashboardReactContextValue = OpenSearchDashboardsReactContextValue < DashboardContainerOptions > ; export type DashboardReactContext = OpenSearchDashboardsReactContext < DashboardContainerOptions > ; export class DashboardContainer extends Container < InheritedChildInput , DashboardContainerInput > { public readonly type = DASHBOARD_CONTAINER_TYPE ; public renderEmpty ? : undefined | ( ( ) => React . ReactNode ) ; private embeddablePanel : EmbeddableStart [ 'EmbeddablePanel' ] ; constructor ( initialInput , private readonly options , stateTransfer ? , parent ? ) { super ( { ... initialInput , } , { embeddableLoaded : { } } , options . embeddable . getEmbeddableFactory , parent ) ; this . embeddablePanel = options . embeddable . getEmbeddablePanel ( stateTransfer ) ; } protected createNewPanelState < TEmbeddableInput extends EmbeddableInput , TEmbeddable extends IEmbeddable < TEmbeddableInput , any > > ( factory , partial = { } ) { const panelState = super . createNewPanelState ( factory , partial ) ; return createPanelState ( panelState , this . input . panels ) ; } public showPlaceholderUntil < TPlacementMethodArgs extends IPanelPlacementArgs > ( newStateComplete , placementMethod ? , placementArgs ? ) { const originalPanelState = { type : PLACEHOLDER_EMBEDDABLE , explicitInput : { id : uuid . v4 ( ) , disabledActions : [ 'ACTION_CUSTOMIZE_PANEL' , 'CUSTOM_TIME_RANGE' , 'clonePanel' , 'replacePanel' , 'togglePanel' , ] , } , } as PanelState < EmbeddableInput > ; const placeholderPanelState = createPanelState ( originalPanelState , this . input . panels , placementMethod , placementArgs ) ; this . updateInput ( { panels : { ... this . input . panels , [ placeholderPanelState . explicitInput . id ] : placeholderPanelState , } , } ) ; newStateComplete . then ( ( newPanelState ) => this . replacePanel ( placeholderPanelState , newPanelState ) ) ; } public replacePanel ( previousPanelState , newPanelState ) { const finalPanels = { ... this . input . panels } ; delete finalPanels [ previousPanelState . explicitInput . id ] ; const newPanelId = newPanelState . explicitInput ?. id ? newPanelState . explicitInput . id : uuid . v4 ( ) ; finalPanels [ newPanelId ] = { ... previousPanelState , ... newPanelState , gridData : { ... previousPanelState . gridData , i : newPanelId , } , explicitInput : { ... newPanelState . explicitInput , id : newPanelId , } , } ; this . updateInput ( { panels : finalPanels , lastReloadRequestTime : new Date ( ) . getTime ( ) , } ) ; } public async addOrUpdateEmbeddable < EEI extends EmbeddableInput = EmbeddableInput , EEO extends EmbeddableOutput = EmbeddableOutput , E extends IEmbeddable < EEI , EEO > = IEmbeddable < EEI , EEO > > ( type , explicitInput , embeddableId ? ) { const idToReplace = embeddableId || explicitInput . id ; if ( idToReplace && this . input . panels [ idToReplace ] ) { this . replacePanel ( this . input . panels [ idToReplace ] , { type , explicitInput : { ... explicitInput , id : uuid . v4 ( ) , } , } ) ; } else { this . addNewEmbeddable < EEI , EEO , E > ( type , explicitInput ) ; } } public render ( dom ) { ReactDOM . render ( < I18nProvider > < OpenSearchDashboardsContextProvider services = { this . options } > < DashboardViewport renderEmpty = { this . renderEmpty } container = { this } PanelComponent = { this . embeddablePanel } / > < /OpenSearchDashboardsContextProvider> < /I18nProvider>, dom); } protected getInheritedInput ( id ) { const { viewMode , refreshConfig , timeRange , query , hidePanelTitles , filters } = this . input ; return { filters , hidePanelTitles , query , timeRange , refreshConfig , viewMode , id , } ; } }