import { EuiBreadcrumb , IconType } from '@elastic/eui' ; import React from 'react' ; import { FormattedMessage } from '@osd/i18n/react' ; import { BehaviorSubject , combineLatest , merge , Observable , of , ReplaySubject } from 'rxjs' ; import { flatMap , map , takeUntil } from 'rxjs/operators' ; import { parse } from 'url' ; import { EuiLink } from '@elastic/eui' ; import { mountReactNode } from '../utils/mount' ; import { InternalApplicationStart } from '../application' ; import { DocLinksStart } from '../doc_links' ; import { HttpStart } from '../http' ; import { InjectedMetadataStart } from '../injected_metadata' ; import { NotificationsStart } from '../notifications' ; import { IUiSettingsClient } from '../ui_settings' ; import { OPENSEARCH_DASHBOARDS_ASK_OPENSEARCH_LINK } from './constants' ; import { ChromeDocTitle , DocTitleService } from './doc_title' ; import { ChromeNavControls , NavControlsService } from './nav_controls' ; import { ChromeNavLinks , NavLinksService , ChromeNavLink } from './nav_links' ; import { ChromeRecentlyAccessed , RecentlyAccessedService } from './recently_accessed' ; import { Header } from './ui' ; import { ChromeHelpExtensionMenuLink } from './ui/header/header_help_menu' ; import { Branding } from '../' ; export { ChromeNavControls , ChromeRecentlyAccessed , ChromeDocTitle } ; const IS_LOCKED_KEY = 'core.chrome.isLocked' ; public export interface ChromeBadge { text : string ; tooltip : string ; iconType ? : IconType ; } public export interface ChromeBrand { logo ? : string ; smallLogo ? : string ; } public export type ChromeBreadcrumb = EuiBreadcrumb ; public export type ChromeBranding = Branding ; public export interface ChromeHelpExtension { /**
     * Provide your plugin's name to create a header for separation
     */ appName : string ; /**
     * Creates unified links for sending users to documentation, GitHub, Discuss, or a custom link/button
     */ links ? : ChromeHelpExtensionMenuLink [ ] ; /**
     * Custom content to occur below the list of links
     */ content ? : ( element : HTMLDivElement ) => ( ) => void ; } interface ConstructorParams { browserSupportsCsp : boolean ; } interface StartDeps { application : InternalApplicationStart ; docLinks : DocLinksStart ; http : HttpStart ; injectedMetadata : InjectedMetadataStart ; notifications : NotificationsStart ; uiSettings : IUiSettingsClient ; } internal export class ChromeService { private isVisible$ ! : Observable < boolean > ; private isForceHidden$ ! : BehaviorSubject < boolean > ; private readonly stop$ = new ReplaySubject ( 1 ) ; private readonly navControls = new NavControlsService ( ) ; private readonly navLinks = new NavLinksService ( ) ; private readonly recentlyAccessed = new RecentlyAccessedService ( ) ; private readonly docTitle = new DocTitleService ( ) ; constructor ( private readonly params ) { } /**
     * These observables allow consumers to toggle the chrome visibility via either:
     *   1. Using setIsVisible() to trigger the next chromeHidden$
     *   2. Setting `chromeless` when registering an application, which will
     *      reset the visibility whenever the next application is mounted
     *   3. Having "embed" in the query string
     */ private initVisibility ( application ) { const isEmbedded = 'embed' in parse ( location . hash . slice ( 1 ) , true ) . query ; this . isForceHidden$ = new BehaviorSubject ( isEmbedded ) ; const appHidden$ = merge ( of ( true ) , application . currentAppId$ . pipe ( flatMap ( ( appId ) => application . applications$ . pipe ( map ( ( applications ) => { return ! ! appId && applications . has ( appId ) && ! ! applications . get ( appId ) ! . chromeless ; } ) ) ) ) ) ; this . isVisible$ = combineLatest ( [ appHidden$ , this . isForceHidden$ ] ) . pipe ( map ( ( [ appHidden , forceHidden ] ) => ! appHidden && ! forceHidden ) , takeUntil ( this . stop$ ) ) ; } public async start ( { application , docLinks , http , injectedMetadata , notifications , uiSettings , } ) { this . initVisibility ( application ) ; const appTitle$ = new BehaviorSubject < string > ( 'Overview' ) ; const brand$ = new BehaviorSubject < ChromeBrand > ( { } ) ; const applicationClasses$ = new BehaviorSubject < Set < string > > ( new Set ( ) ) ; const helpExtension$ = new BehaviorSubject < ChromeHelpExtension | undefined > ( undefined ) ; const breadcrumbs$ = new BehaviorSubject < ChromeBreadcrumb [ ] > ( [ ] ) ; const badge$ = new BehaviorSubject < ChromeBadge | undefined > ( undefined ) ; const customNavLink$ = new BehaviorSubject < ChromeNavLink | undefined > ( undefined ) ; const helpSupportUrl$ = new BehaviorSubject < string > ( OPENSEARCH_DASHBOARDS_ASK_OPENSEARCH_LINK ) ; const isNavDrawerLocked$ = new BehaviorSubject ( localStorage . getItem ( IS_LOCKED_KEY ) === 'true' ) ; const navControls = this . navControls . start ( ) ; const navLinks = this . navLinks . start ( { application , http } ) ; const recentlyAccessed = await this . recentlyAccessed . start ( { http } ) ; const docTitle = this . docTitle . start ( { document : window . document } ) ; application . currentAppId$ . subscribe ( ( ) => { helpExtension$ . next ( undefined ) ; breadcrumbs$ . next ( [ ] ) ; badge$ . next ( undefined ) ; docTitle . reset ( ) ; } ) ; const setIsNavDrawerLocked = ( isLocked ) => { isNavDrawerLocked$ . next ( isLocked ) ; localStorage . setItem ( IS_LOCKED_KEY , `${ isLocked }` ) ; } ; const getIsNavDrawerLocked$ = isNavDrawerLocked$ . pipe ( takeUntil ( this . stop$ ) ) ; const isIE = ( ) => { const ua = window . navigator . userAgent ; const msie = ua . indexOf ( 'MSIE ' ) ; const trident = ua . indexOf ( 'Trident/' ) ; return msie > 0 || trident > 0 ; } ; if ( ! this . params . browserSupportsCsp && injectedMetadata . getCspConfig ( ) . warnLegacyBrowsers ) { notifications . toasts . addWarning ( { title : mountReactNode ( < FormattedMessage id = "core.chrome.legacyBrowserWarning" defaultMessage = "Your browser does not meet the security requirements for OpenSearch Dashboards." / > ) , } ) ; } if ( isIE ( ) ) { notifications . toasts . addWarning ( { title : mountReactNode ( < FormattedMessage id = "core.chrome.browserDeprecationWarning" defaultMessage = "Support for Internet Explorer will be dropped in future versions of this software, please check {link}." values = { { link : ( < EuiLink target = "_blank" href = { docLinks . links . opensearchDashboards . browser } external > < FormattedMessage id = "core.chrome.browserDeprecationLink" defaultMessage = "the support matrix on our website" / > < /EuiLink>), } } />), } ) ; } return { navControls , navLinks , recentlyAccessed , docTitle , getHeaderComponent : ( ) => ( < Header loadingCount$ = { http . getLoadingCount$ ( ) } application = { application } appTitle$ = { appTitle$ . pipe ( takeUntil ( this . stop$ ) ) } badge$ = { badge$ . pipe ( takeUntil ( this . stop$ ) ) } basePath = { http . basePath } breadcrumbs$ = { breadcrumbs$ . pipe ( takeUntil ( this . stop$ ) ) } customNavLink$ = { customNavLink$ . pipe ( takeUntil ( this . stop$ ) ) } opensearchDashboardsDocLink = { docLinks . links . opensearchDashboards . introduction } forceAppSwitcherNavigation$ = { navLinks . getForceAppSwitcherNavigation$ ( ) } helpExtension$ = { helpExtension$ . pipe ( takeUntil ( this . stop$ ) ) } helpSupportUrl$ = { helpSupportUrl$ . pipe ( takeUntil ( this . stop$ ) ) } homeHref = { http . basePath . prepend ( '/app/home' ) } isVisible$ = { this . isVisible$ } opensearchDashboardsVersion = { injectedMetadata . getOpenSearchDashboardsVersion ( ) } navLinks$ = { navLinks . getNavLinks$ ( ) } recentlyAccessed$ = { recentlyAccessed . get$ ( ) } navControlsLeft$ = { navControls . getLeft$ ( ) } navControlsCenter$ = { navControls . getCenter$ ( ) } navControlsRight$ = { navControls . getRight$ ( ) } onIsLockedUpdate = { setIsNavDrawerLocked } isLocked$ = { getIsNavDrawerLocked$ } branding = { injectedMetadata . getBranding ( ) } / > ) , setAppTitle : ( appTitle ) => appTitle$ . next ( appTitle ) , getBrand$ : ( ) => brand$ . pipe ( takeUntil ( this . stop$ ) ) , setBrand : ( brand ) => { brand$ . next ( Object . freeze ( { logo : brand . logo , smallLogo : brand . smallLogo , } ) ) ; } , getIsVisible$ : ( ) => this . isVisible$ , setIsVisible : ( isVisible ) => this . isForceHidden$ . next ( ! isVisible ) , getApplicationClasses$ : ( ) => applicationClasses$ . pipe ( map ( ( set ) => [ ... set ] ) , takeUntil ( this . stop$ ) ) , addApplicationClass : ( className ) => { const update = new Set ( [ ... applicationClasses$ . getValue ( ) ] ) ; update . add ( className ) ; applicationClasses$ . next ( update ) ; } , removeApplicationClass : ( className ) => { const update = new Set ( [ ... applicationClasses$ . getValue ( ) ] ) ; update . delete ( className ) ; applicationClasses$ . next ( update ) ; } , getBadge$ : ( ) => badge$ . pipe ( takeUntil ( this . stop$ ) ) , setBadge : ( badge ) => { badge$ . next ( badge ) ; } , getBreadcrumbs$ : ( ) => breadcrumbs$ . pipe ( takeUntil ( this . stop$ ) ) , setBreadcrumbs : ( newBreadcrumbs ) => { breadcrumbs$ . next ( newBreadcrumbs ) ; } , getHelpExtension$ : ( ) => helpExtension$ . pipe ( takeUntil ( this . stop$ ) ) , setHelpExtension : ( helpExtension ? ) => { helpExtension$ . next ( helpExtension ) ; } , setHelpSupportUrl : ( url ) => helpSupportUrl$ . next ( url ) , getIsNavDrawerLocked$ : ( ) => getIsNavDrawerLocked$ , getCustomNavLink$ : ( ) => customNavLink$ . pipe ( takeUntil ( this . stop$ ) ) , setCustomNavLink : ( customNavLink ? ) => { customNavLink$ . next ( customNavLink ) ; } , } ; } public stop ( ) { this . navLinks . stop ( ) ; this . stop$ . next ( ) ; } } remarks example example public export interface ChromeStart { /** {@inheritdoc ChromeNavLinks} */ navLinks : ChromeNavLinks ; /** {@inheritdoc ChromeNavControls} */ navControls : ChromeNavControls ; /** {@inheritdoc ChromeRecentlyAccessed} */ recentlyAccessed : ChromeRecentlyAccessed ; /** {@inheritdoc ChromeDocTitle} */ docTitle : ChromeDocTitle ; internalRemarks setAppTitle ( appTitle ) : void ; /**
     * Get an observable of the current brand information.
     */ getBrand$ ( ) : Observable < ChromeBrand > ; remarks example setBrand ( brand ) : void ; /**
     * Get an observable of the current visibility state of the chrome.
     */ getIsVisible$ ( ) : Observable < boolean > ; /**
     * Set the temporary visibility for the chrome. This does nothing if the chrome is hidden
     * by default and should be used to hide the chrome for things like full-screen modes
     * with an exit button.
     */ setIsVisible ( isVisible ) : void ; /**
     * Get the current set of classNames that will be set on the application container.
     */ getApplicationClasses$ ( ) : Observable < string [ ] > ; /**
     * Add a className that should be set on the application container.
     */ addApplicationClass ( className ) : void ; /**
     * Remove a className added with `addApplicationClass()`. If className is unknown it is ignored.
     */ removeApplicationClass ( className ) : void ; /**
     * Get an observable of the current badge
     */ getBadge$ ( ) : Observable < ChromeBadge | undefined > ; /**
     * Override the current badge
     */ setBadge ( badge ? ) : void ; /**
     * Get an observable of the current list of breadcrumbs
     */ getBreadcrumbs$ ( ) : Observable < ChromeBreadcrumb [ ] > ; /**
     * Override the current set of breadcrumbs
     */ setBreadcrumbs ( newBreadcrumbs ) : void ; /**
     * Get an observable of the current custom nav link
     */ getCustomNavLink$ ( ) : Observable < Partial < ChromeNavLink > | undefined > ; /**
     * Override the current set of custom nav link
     */ setCustomNavLink ( newCustomNavLink ? ) : void ; /**
     * Get an observable of the current custom help conttent
     */ getHelpExtension$ ( ) : Observable < ChromeHelpExtension | undefined > ; /**
     * Override the current set of custom help content
     */ setHelpExtension ( helpExtension ? ) : void ; param url setHelpSupportUrl ( url ) : void ; /**
     * Get an observable of the current locked state of the nav drawer.
     */ getIsNavDrawerLocked$ ( ) : Observable < boolean > ; } internal export interface InternalChromeStart extends ChromeStart { internal getHeaderComponent ( ) : JSX . Element ; }