'use strict' ; import type { AlignValue , VerticalAlignValue } from '../Core/Renderer/AlignObject' ; import type ButtonThemeObject from '../Core/Renderer/SVG/ButtonThemeObject' ; import type { CSSObject } from '../Core/Renderer/CSSObject' ; import type { ButtonRelativeToValue } from '../Maps/MapNavigationOptions' ; import type SVGElement from '../Core/Renderer/SVG/SVGElement' ; import Chart from '../Core/Chart/Chart.js' ; import H from '../Core/Globals.js' ; import D from '../Core/DefaultOptions.js' ; const { defaultOptions } = D ; import { Palette } from '../Core/Color/Palettes.js' ; import U from '../Core/Utilities.js' ; import F from '../Core/FormatUtilities.js' ; import SVGAttributes from '../Core/Renderer/SVG/SVGAttributes' ; import SeriesOptions from '../Core/Series/SeriesOptions' ; import type { PointOptions , PointShortOptions } from '../Core/Series/PointOptions' ; const { format } = F ; const { addEvent , objectEach , extend , fireEvent , merge , pick , defined , isString } = U ; declare module '../Core/Chart/ChartLike' { interface ChartLike { breadcrumbsBottomMargin ? : boolean ; breadcrumbsTopMargin ? : boolean ; breadcrumbs ? : Breadcrumbs ; } } declare module '../Core/LangOptions' { interface LangOptions { breadcrumbsToLabel ? : string ; mainBreadcrumb ? : string ; } } declare module '../Series/Treemap/TreemapSeriesOptions' { interface TreemapSeriesOptions { breadcrumbs ? : Breadcrumbs . BreadcrumbsOptions ; } } private declare global { namespace Highcharts { interface DrilldownOptions { breadcrumbs ? : Breadcrumbs . BreadcrumbsOptions ; } class Breadcrumbs { public constructor ( chart , userOptions ? ) ; chart : Chart ; level : number ; isDirty : boolean ; yOffset ? : number ; getLevel ( this ) ; setList ( this , list ) ; setLevel ( this ) ; updateProperties ( this , list ) ; } } } extend ( defaultOptions . lang , { since product mainBreadcrumb : 'Main' } ) ; private class name param { Highcharts . Chart } chart param { Highcharts . Options } userOptions class Breadcrumbs { since product optionparent public static defaultBreadcrumbsOptions = { sample type { Highcharts . SVGAttributes } since product buttonTheme : { ignore fill : 'none' , ignore height : 18 , ignore padding : 2 , ignore 'stroke-width' : 0 , ignore zIndex : 7 , ignore states : { select : { fill : 'none' } } , style : { color : Palette . highlightColor80 } } , type { number } since buttonSpacing : 5 , type { Highcharts . BreadcrumbsClickCallbackFunction } since apioption sample type { boolean } since floating : false , type { string | undefined } since default sample format : void 0 , type { Highcharts . BreadcrumbsFormatterCallbackFunction } since default apioption type { Highcharts . ButtonRelativeToValue } default since product relativeTo : 'plotBox' , type { Highcharts . BreadcrumbsAlignOptions } since product sample position : { type { Highcharts . AlignValue } align : 'left' , type { Highcharts . VerticalAlignValue } verticalAlign : 'top' , type { number } x : 0 , type { number | undefined } y : void 0 } , since separator : { type { string } since product text : '/' , type { Highcharts . CSSObject } since style : { color : Palette . neutralColor60 } } , type { boolean } since sample showFullPath : true , type { Highcharts . SVGAttributes } since style : { } , type { boolean } since useHTML : false , type { number } since zIndex : 7 } ; public group ? : SVGElement = void 0 ; public list : Array < Breadcrumbs . BreadcrumbOptions > = [ ] ; public elementList : { [ x ] : Breadcrumbs . BreadcrumbElement ; } = { } ; public chart : Chart ; public isDirty : boolean = true ; public level : number = 0 ; public options : Breadcrumbs . BreadcrumbsOptions = void 0 as any ; public yOffset ? : number ; public constructor ( chart , userOptions ? ) { const chartOptions = merge ( chart . options . drilldown && chart . options . drilldown . drillUpButton , Breadcrumbs . defaultBreadcrumbsOptions , userOptions ) ; this . chart = chart ; this . options = chartOptions || { } ; } requires function param { Highcharts . Breadcrumbs } this public updateProperties ( list ) { this . setList ( list ) ; this . setLevel ( ) ; this . isDirty = true ; } function requires param { Highcharts . Breadcrumbs } this param { Highcharts . BreadcrumbsOptions } list public setList ( list ) { this . list = list ; } requires function param { Highcharts . Breadcrumbs } this public setLevel ( ) { this . level = this . list . length && this . list . length - 1 ; } requires function param { Highcharts . Breadcrumbs } this public getLevel ( ) { return this . level ; } requires function param { Highcharts . Breadcrumbs } this param { Highcharts . Breadcrumbs } breadcrumb return { string } public getButtonText ( breadcrumb ) { const breadcrumbs = this , chart = breadcrumbs . chart , breadcrumbsOptions = breadcrumbs . options , lang = chart . options . lang , textFormat = pick ( breadcrumbsOptions . format , breadcrumbsOptions . showFullPath ? '{level.name}' : '← {level.name}' ) , defaultText = lang && pick ( lang . drillUpText , lang . mainBreadcrumb ) ; let returnText = breadcrumbsOptions . formatter && breadcrumbsOptions . formatter ( breadcrumb ) || format ( textFormat , { level : breadcrumb . levelOptions } , chart ) || '' ; if ( ( ( isString ( returnText ) && ! returnText . length ) || returnText === '← ' ) && defined ( defaultText ) ) { returnText = ! breadcrumbsOptions . showFullPath ? '← ' + defaultText : defaultText ; } return returnText ; } requires function param { Highcharts . Breadcrumbs } this public redraw ( ) { if ( this . isDirty ) { this . render ( ) ; } if ( this . group ) { this . group . align ( ) ; } this . isDirty = false ; } requires function param { Highcharts . Breadcrumbs } this public render ( ) { const breadcrumbs = this , chart = breadcrumbs . chart , breadcrumbsOptions = breadcrumbs . options ; if ( ! breadcrumbs . group && breadcrumbsOptions ) { breadcrumbs . group = chart . renderer . g ( 'breadcrumbs-group' ) . addClass ( 'highcharts-no-tooltip highcharts-breadcrumbs' ) . attr ( { zIndex : breadcrumbsOptions . zIndex } ) . add ( ) ; } if ( breadcrumbsOptions . showFullPath ) { this . renderFullPathButtons ( ) ; } else { this . renderSingleButton ( ) ; } this . alignBreadcrumbsGroup ( ) ; } requires function param { Highcharts . Breadcrumbs } this public renderFullPathButtons ( ) { this . destroySingleButton ( ) ; this . resetElementListState ( ) ; this . updateListElements ( ) ; this . destroyListElements ( ) ; } requires function param { Highcharts . Breadcrumbs } this public renderSingleButton ( ) { const breadcrumbs = this , chart = breadcrumbs . chart , list = breadcrumbs . list , breadcrumbsOptions = breadcrumbs . options , buttonSpacing = breadcrumbsOptions . buttonSpacing ; this . destroyListElements ( ) ; const posX = breadcrumbs . group ? breadcrumbs . group . getBBox ( ) . width : buttonSpacing , posY = buttonSpacing ; const previousBreadcrumb = list [ list . length - 2 ] ; if ( ! chart . drillUpButton && ( this . level > 0 ) ) { chart . drillUpButton = breadcrumbs . renderButton ( previousBreadcrumb , posX , posY ) ; } else if ( chart . drillUpButton ) { if ( this . level > 0 ) { this . updateSingleButton ( ) ; } else { this . destroySingleButton ( ) ; } } } requires function param { Highcharts . Breadcrumbs } this public alignBreadcrumbsGroup ( xOffset ? ) { const breadcrumbs = this ; if ( breadcrumbs . group ) { const breadcrumbsOptions = breadcrumbs . options , buttonTheme = breadcrumbsOptions . buttonTheme , positionOptions = breadcrumbsOptions . position , alignTo = ( breadcrumbsOptions . relativeTo === 'chart' || breadcrumbsOptions . relativeTo === 'spacingBox' ? void 0 : 'scrollablePlotBox' ) , bBox = breadcrumbs . group . getBBox ( ) , additionalSpace = 2 * ( buttonTheme . padding || 0 ) + breadcrumbsOptions . buttonSpacing ; positionOptions . width = bBox . width + additionalSpace ; positionOptions . height = bBox . height + additionalSpace ; const newPositions = merge ( positionOptions ) ; if ( xOffset ) { newPositions . x += xOffset ; } newPositions . y = pick ( newPositions . y , this . yOffset , 0 ) ; breadcrumbs . group . align ( newPositions , true , alignTo ) ; } } requires function param { Highcharts . Breadcrumbs } this param { Highcharts . Breadcrumbs } breadcrumb param { Highcharts . Breadcrumbs } posX param { Highcharts . Breadcrumbs } posY return { SVGElement | void } public renderButton ( breadcrumb , posX , posY ) { const breadcrumbs = this , chart = this . chart , breadcrumbsOptions = breadcrumbs . options , buttonTheme = merge ( breadcrumbsOptions . buttonTheme ) , states = buttonTheme . states ; delete buttonTheme . states ; const button = chart . renderer . button ( breadcrumbs . getButtonText ( breadcrumb ) , posX , posY , function ( e ) : void { const buttonEvents = breadcrumbsOptions . events && breadcrumbsOptions . events . click ; let callDefaultEvent ; if ( buttonEvents ) { callDefaultEvent = buttonEvents . call ( breadcrumbs , e as any , breadcrumb ) ; } if ( callDefaultEvent !== false ) { if ( ! breadcrumbsOptions . showFullPath ) { e . newLevel = breadcrumbs . level - 1 ; } else { e . newLevel = breadcrumb . level ; } fireEvent ( breadcrumbs , 'up' , e ) ; } } , buttonTheme , states && states . hover , states && states . select , states && states . disabled ) . addClass ( 'highcharts-breadcrumbs-button' ) . add ( breadcrumbs . group ) ; if ( ! chart . styledMode ) { button . attr ( breadcrumbsOptions . style as SVGAttributes ) ; } return button ; } requires function param { Highcharts . Breadcrumbs } this param { Highcharts . Breadcrumbs } posX param { Highcharts . Breadcrumbs } posY return { Highcharts . SVGElement } public renderSeparator ( posX , posY ) { const breadcrumbs = this , chart = this . chart , breadcrumbsOptions = breadcrumbs . options , separatorOptions = breadcrumbsOptions . separator ; const separator = chart . renderer . label ( separatorOptions . text , posX , posY , void 0 , void 0 , void 0 , false ) . addClass ( 'highcharts-breadcrumbs-separator' ) . add ( breadcrumbs . group ) ; if ( ! chart . styledMode ) { separator . css ( separatorOptions . style ) ; } return separator ; } function requires param { Highcharts . Breadcrumbs } this param { Highcharts . BreadcrumbsOptions } options param { boolean } redraw public update ( options ) { merge ( true , this . options , options ) ; this . destroy ( ) ; this . isDirty = true ; } function requires param { Highcharts . Breadcrumbs } this public updateSingleButton ( ) { const chart = this . chart , currentBreadcrumb = this . list [ this . level - 1 ] ; if ( chart . drillUpButton ) { chart . drillUpButton . attr ( { text : this . getButtonText ( currentBreadcrumb ) } ) ; } } requires function param { Highcharts . Breadcrumbs } this public destroy ( ) { this . destroySingleButton ( ) ; this . destroyListElements ( true ) ; if ( this . group ) { this . group . destroy ( ) ; } this . group = void 0 ; } requires function param { Highcharts . Breadcrumbs } this public destroyListElements ( force ? ) { const elementList = this . elementList ; objectEach ( elementList , ( element , level ) : void => { if ( force || ! elementList [ level ] . updated ) { element = elementList [ level ] ; element . button && element . button . destroy ( ) ; element . separator && element . separator . destroy ( ) ; delete element . button ; delete element . separator ; delete elementList [ level ] ; } } ) ; if ( force ) { this . elementList = { } ; } } requires function param { Highcharts . Breadcrumbs } this public destroySingleButton ( ) { if ( this . chart . drillUpButton ) { this . chart . drillUpButton . destroy ( ) ; this . chart . drillUpButton = void 0 ; } } requires function param { Highcharts . Breadcrumbs } this public resetElementListState ( ) { objectEach ( this . elementList , ( element ) : void => { element . updated = false ; } ) ; } requires function param { Highcharts . Breadcrumbs } this public updateListElements ( ) { const updateXPosition = function ( element , spacing ) : number { return element . getBBox ( ) . width + spacing ; } , breadcrumbs = this , elementList = breadcrumbs . elementList , buttonSpacing = breadcrumbs . options . buttonSpacing , list = breadcrumbs . list ; let posX = breadcrumbs . group ? updateXPosition ( breadcrumbs . group , buttonSpacing ) : buttonSpacing , posY = buttonSpacing , currentBreadcrumb ; list . forEach ( function ( breadcrumb , index ) : void { const isLast = index === list . length - 1 ; let button , separator ; if ( elementList [ breadcrumb . level ] ) { currentBreadcrumb = elementList [ breadcrumb . level ] ; button = currentBreadcrumb . button ; if ( ! currentBreadcrumb . separator && ! isLast ) { posX += buttonSpacing ; currentBreadcrumb . separator = breadcrumbs . renderSeparator ( posX , posY ) ; posX += updateXPosition ( currentBreadcrumb . separator , buttonSpacing ) ; } else if ( currentBreadcrumb . separator && isLast ) { currentBreadcrumb . separator . destroy ( ) ; delete currentBreadcrumb . separator ; } elementList [ breadcrumb . level ] . updated = true ; } else { button = breadcrumbs . renderButton ( breadcrumb , posX , posY ) ; posX += updateXPosition ( button , buttonSpacing ) ; if ( ! isLast ) { separator = breadcrumbs . renderSeparator ( posX , posY ) ; posX += updateXPosition ( separator , buttonSpacing ) ; } elementList [ breadcrumb . level ] = { button , separator , updated : true } ; } if ( button ) { button . setState ( isLast ? 2 : 0 ) ; } } ) ; } } if ( ! H . Breadcrumbs ) { H . Breadcrumbs = Breadcrumbs as typeof Breadcrumbs ; addEvent ( Chart , 'getMargins' , function ( ) : void { const breadcrumbs = this . breadcrumbs ; if ( breadcrumbs && ! breadcrumbs . options . floating && breadcrumbs . level ) { const breadcrumbsOptions = breadcrumbs . options , buttonTheme = breadcrumbsOptions . buttonTheme , breadcrumbsHeight = ( ( buttonTheme . height || 0 ) + 2 * ( buttonTheme . padding || 0 ) + breadcrumbsOptions . buttonSpacing ) , verticalAlign = breadcrumbsOptions . position . verticalAlign ; if ( verticalAlign === 'bottom' ) { this . marginBottom = ( this . marginBottom || 0 ) + breadcrumbsHeight ; breadcrumbs . yOffset = breadcrumbsHeight ; } else if ( verticalAlign !== 'middle' ) { this . plotTop += breadcrumbsHeight ; breadcrumbs . yOffset = - breadcrumbsHeight ; } else { breadcrumbs . yOffset = void 0 ; } } } ) ; addEvent ( Chart , 'redraw' , function ( ) : void { this . breadcrumbs && this . breadcrumbs . redraw ( ) ; } ) ; addEvent ( Chart , 'destroy' , function destroyEvents ( ) : void { if ( this . breadcrumbs ) { this . breadcrumbs . destroy ( ) ; this . breadcrumbs = void 0 as Breadcrumbs | undefined ; } } ) ; addEvent ( Chart , 'afterShowResetZoom' , function ( ) : void { const chart = this ; if ( chart . breadcrumbs ) { const bbox = chart . resetZoomButton && chart . resetZoomButton . getBBox ( ) , breadcrumbsOptions = chart . breadcrumbs . options ; if ( bbox && breadcrumbsOptions . position . align === 'right' && breadcrumbsOptions . relativeTo === 'plotBox' ) { chart . breadcrumbs . alignBreadcrumbsGroup ( - bbox . width - breadcrumbsOptions . buttonSpacing ) ; } } } ) ; addEvent ( Chart , 'selection' , function ( event ) : void { if ( event . resetSelection === true && this . breadcrumbs ) { this . breadcrumbs . alignBreadcrumbsGroup ( ) ; } } ) ; } namespace Breadcrumbs { export interface BreadcrumbsOptions { buttonTheme : ButtonThemeObject ; buttonSpacing : number ; events ? : BreadcrumbsButtonsEventsOptions ; floating : boolean ; format ? : string ; formatter ? : BreadcrumbsButtonsFormatter ; relativeTo ? : ButtonRelativeToValue ; position : BreadcrumbsAlignOptions ; separator : SeparatorOptions ; showFullPath : boolean ; style : CSSObject ; useHTML : boolean ; zIndex : number ; } export type BreadcrumbOptions = { level : number ; levelOptions : SeriesOptions | PointOptions | PointShortOptions ; } ; export type BreadcrumbElement = { button ? : SVGElement ; separator ? : SVGElement ; updated ? : boolean ; } ; export interface BreadcrumbsAlignOptions { align : AlignValue ; verticalAlign : VerticalAlignValue ; x : number ; y : number ; width ? : number ; height ? : number ; } export interface BreadcrumbsClickCallbackFunction { ( e , breadcrumb ) : ( boolean | undefined ) ; } export interface BreadcrumbsButtonsEventsOptions { click ? : BreadcrumbsClickCallbackFunction ; } export interface BreadcrumbsButtonsFormatter { ( breadcrumb ) : ( string ) ; } export interface SeparatorOptions { text : string ; style : CSSObject ; } } export default Breadcrumbs ; callback Highcharts . BreadcrumbsClickCallbackFunction param { Highcharts . Event } event param { Highcharts . BreadcrumbOptions } options param { global . Event } e callback Highcharts . BreadcrumbsFormatterCallbackFunction param { Highcharts . Event } event param { Highcharts . BreadcrumbOptions } options return { string } interface name type { number } name type { SeriesOptions | PointOptionsObject } interface default name type { AlignValue } default name type { VerticalAlignValue } name type { number } name type { number } interface name type { SVGAttributes | BreadcrumbsButtonThemeOptions | undefined } ( '' ) ;