'use strict' ; import type BubbleSeriesOptions from './BubbleSeriesOptions' ; import type { StatesOptionsKey } from '../../Core/Series/StatesOptions' ; import type SVGAttributes from '../../Core/Renderer/SVG/SVGAttributes' ; import Axis from '../../Core/Axis/Axis.js' ; import BubbleLegendComposition from './BubbleLegendComposition.js' ; import BubblePoint from './BubblePoint.js' ; import Color from '../../Core/Color/Color.js' ; const { parse : color } = Color ; import H from '../../Core/Globals.js' ; const { noop } = H ; import Point from '../../Core/Series/Point.js' ; import Series from '../../Core/Series/Series.js' ; import SeriesRegistry from '../../Core/Series/SeriesRegistry.js' ; const { seriesTypes : { column : ColumnSeries , scatter : ScatterSeries } } = SeriesRegistry ; import U from '../../Core/Utilities.js' ; const { addEvent , arrayMax , arrayMin , clamp , extend , isNumber , merge , pick } = U ; import '../Column/ColumnSeries.js' ; import '../Scatter/ScatterSeries.js' ; import './BubbleLegendItem.js' ; declare module '../../Core/Chart/ChartLike' { interface ChartLike { bubbleZExtremes ? : BubbleZExtremes ; } } declare module '../../Core/Axis/AxisLike' { interface AxisLike { beforePadding ? ( ) : void ; } } declare module '../../Core/Series/SeriesLike' { interface SeriesLike { bubblePadding ? : BubbleSeries [ 'bubblePadding' ] ; radii ? : BubbleSeries [ 'radii' ] ; specialGroup ? : BubbleSeries [ 'specialGroup' ] ; zData ? : BubbleSeries [ 'zData' ] ; } } type BubblePxExtremes = { minPxSize : number ; maxPxSize : number ; } ; type BubbleZExtremes = { zMin : number ; zMax : number ; } ; class BubbleSeries extends ScatterSeries { public static compose = BubbleLegendComposition . compose ; sample extends plotOptions . scatter excluding product requires optionparent public static defaultOptions : BubbleSeriesOptions = merge ( ScatterSeries . defaultOptions , { dataLabels : { formatter : function ( this ) : string { const { numberFormatter } = this . series . chart ; const { z } = ( this . point as BubblePoint ) ; return isNumber ( z ) ? numberFormatter ( z , - 1 ) : '' ; } , inside : true , verticalAlign : 'middle' } , since animationLimit : 250 , sample type { boolean } default since apioption extends plotOptions . series . marker excluding marker : { lineColor : null as any , lineWidth : 1 , /**
             * The fill opacity of the bubble markers.
             */ fillOpacity : 0.5 , ignore-option radius : null as any , states : { hover : { radiusPlus : 0 } } , sample sample type { Highcharts . SymbolKeyValue | string } since symbol : 'circle' } , sample type { number | string } since product minSize : 8 , sample type { number | string } since product maxSize : '20%' , sample type { Highcharts . ColorString | Highcharts . GradientColorObject | Highcharts . PatternObject } since product apioption sample type { Highcharts . BubbleSizeByValue } default since apioption sample type { boolean } default since product apioption since product softThreshold : false , states : { hover : { halo : { size : 5 } } } , tooltip : { pointFormat : '({point.x}, {point.y}), Size: {point.z}' } , turboThreshold : 0 , see sample type { number } since product apioption default apioption see sample type { number } since product apioption sample since product zThreshold : 0 , zoneAxis : 'z' } as BubbleSeriesOptions ) ; public data : Array < BubblePoint > = void 0 as any ; public displayNegative : BubbleSeriesOptions [ 'displayNegative' ] ; public maxPxSize : number = void 0 as any ; public minPxSize : number = void 0 as any ; public options : BubbleSeriesOptions = void 0 as any ; public points : Array < BubblePoint > = void 0 as any ; public radii : Array < ( number | null ) > = void 0 as any ; public yData : Array < ( number | null ) > = void 0 as any ; public zData : Array < ( number | null ) > = void 0 as any ; public zMax : BubbleSeriesOptions [ 'zMax' ] ; public zMin : BubbleSeriesOptions [ 'zMin' ] ; private public animate ( init ? ) { if ( ! init && this . points . length < ( this . options . animationLimit as any ) ) { this . points . forEach ( function ( point ) : void { const { graphic } = point ; if ( graphic && graphic . width ) { if ( ! this . hasRendered ) { graphic . attr ( { x : point . plotX , y : point . plotY , width : 1 , height : 1 } ) ; } graphic . animate ( this . markerAttribs ( point ) , this . options . animation ) ; } } , this ) ; } } private public getRadii ( ) { let len , i , zData = this . zData , yData = this . yData , radii = [ ] as Array < ( number | null ) > , value , zExtremes = this . chart . bubbleZExtremes ; const { minPxSize , maxPxSize } = this . getPxExtremes ( ) ; if ( ! zExtremes ) { let zMin = Number . MAX_VALUE ; let zMax = - Number . MAX_VALUE ; let valid ; this . chart . series . forEach ( ( otherSeries ) : void => { if ( otherSeries . bubblePadding && ( otherSeries . visible || ! this . chart . options . chart . ignoreHiddenSeries ) ) { const zExtremes = ( otherSeries as any ) . getZExtremes ( ) ; if ( zExtremes ) { zMin = Math . min ( zMin || zExtremes . zMin , zExtremes . zMin ) ; zMax = Math . max ( zMax || zExtremes . zMax , zExtremes . zMax ) ; valid = true ; } } } ) ; if ( valid ) { zExtremes = { zMin , zMax } ; this . chart . bubbleZExtremes = zExtremes ; } else { zExtremes = { zMin : 0 , zMax : 0 } ; } } for ( i = 0 , len = zData . length ; i < len ; i ++ ) { value = zData [ i ] ; radii . push ( this . getRadius ( zExtremes . zMin , zExtremes . zMax , minPxSize , maxPxSize , value , yData [ i ] ) ) ; } this . radii = radii ; } private public getRadius ( zMin , zMax , minSize , maxSize , value , yValue ? ) { let options = this . options , sizeByArea = options . sizeBy !== 'width' , zThreshold = options . zThreshold , zRange = zMax - zMin , pos = 0.5 ; if ( yValue === null || value === null ) { return null ; } if ( isNumber ( value ) ) { if ( options . sizeByAbsoluteValue ) { value = Math . abs ( value - ( zThreshold as any ) ) ; zMax = zRange = Math . max ( zMax - ( zThreshold as any ) , Math . abs ( zMin - ( zThreshold as any ) ) ) ; zMin = 0 ; } if ( value < zMin ) { return minSize / 2 - 1 ; } if ( zRange > 0 ) { pos = ( value - zMin ) / zRange ; } } if ( sizeByArea && pos >= 0 ) { pos = Math . sqrt ( pos ) ; } return Math . ceil ( minSize + pos * ( maxSize - minSize ) ) / 2 ; } private public hasData ( ) { return ! ! this . processedXData . length ; } private public pointAttribs ( point ? , state ? ) { const markerOptions = this . options . marker , fillOpacity = ( markerOptions as any ) . fillOpacity , attr = Series . prototype . pointAttribs . call ( this , point , state ) ; if ( fillOpacity !== 1 ) { attr . fill = color ( attr . fill as any ) . setOpacity ( fillOpacity ) . get ( 'rgba' ) ; } return attr ; } private public translate ( ) { super . translate . call ( this ) ; this . getRadii ( ) ; this . translateBubble ( ) ; } public translateBubble ( ) { const { data , radii } = this ; const { minPxSize } = this . getPxExtremes ( ) ; let i = data . length ; while ( i -- ) { const point = data [ i ] ; const radius = radii ? radii [ i ] : 0 ; if ( isNumber ( radius ) && radius >= minPxSize / 2 ) { point . marker = extend ( point . marker , { radius , width : 2 * radius , height : 2 * radius } ) ; point . dlBox = { x : ( point . plotX as any ) - radius , y : ( point . plotY as any ) - radius , width : 2 * radius , height : 2 * radius } ; } else { point . shapeArgs = point . plotY = point . dlBox = void 0 ; } } } public getPxExtremes ( ) { const smallestSize = Math . min ( this . chart . plotWidth , this . chart . plotHeight ) ; const getPxSize = ( length ) : number => { let isPercent ; if ( typeof length === 'string' ) { isPercent = /%$/ . test ( length ) ; length = parseInt ( length , 10 ) ; } return isPercent ? smallestSize * length / 100 : length ; } ; const minPxSize = getPxSize ( pick ( this . options . minSize , 8 ) ) ; const maxPxSize = Math . max ( getPxSize ( pick ( this . options . maxSize , '20%' ) ) , minPxSize ) ; return { minPxSize , maxPxSize } ; } public getZExtremes ( ) { const options = this . options , zData = ( this . zData || [ ] ) . filter ( isNumber ) ; if ( zData . length ) { const zMin = pick ( options . zMin , clamp ( arrayMin ( zData ) , options . displayNegative === false ? ( options . zThreshold || 0 ) : - Number . MAX_VALUE , Number . MAX_VALUE ) ) ; const zMax = pick ( options . zMax , arrayMax ( zData ) ) ; if ( isNumber ( zMin ) && isNumber ( zMax ) ) { return { zMin , zMax } ; } } } } interface BubbleSeries { alignDataLabel : typeof ColumnSeries . prototype [ 'alignDataLabel' ] ; bubblePadding : boolean ; isBubble : true ; pointClass : typeof BubblePoint ; specialGroup : string ; zoneAxis : string ; } extend ( BubbleSeries . prototype , { alignDataLabel : ColumnSeries . prototype . alignDataLabel , applyZones : noop , bubblePadding : true , buildKDTree : noop , directTouch : true , isBubble : true , pointArrayMap : [ 'y' , 'z' ] , pointClass : BubblePoint , parallelArrays : [ 'x' , 'y' , 'z' ] , trackerGroups : [ 'group' , 'dataLabelsGroup' ] , specialGroup : 'group' , zoneAxis : 'z' } ) ; addEvent ( BubbleSeries , 'updatedData' , ( e ) : void => { delete e . target . chart . bubbleZExtremes ; } ) ; Axis . prototype . beforePadding = function ( ) : void { let axis = this , axisLength = this . len , chart = this . chart , pxMin = 0 , pxMax = axisLength , isXAxis = this . isXAxis , dataKey = isXAxis ? 'xData' : 'yData' , min = this . min , range = ( this . max as any ) - ( min as any ) , transA = axisLength / range , hasActiveSeries ; this . series . forEach ( function ( series ) : void { if ( series . bubblePadding && ( series . visible || ! chart . options . chart . ignoreHiddenSeries ) ) { axis . allowZoomOutside = true ; hasActiveSeries = true ; const data = ( series as any ) [ dataKey ] ; if ( isXAxis ) { ( series as any ) . getRadii ( 0 , 0 , series ) ; } if ( range > 0 ) { let i = data . length ; while ( i -- ) { if ( isNumber ( data [ i ] ) && ( axis . dataMin as any ) <= data [ i ] && data [ i ] <= ( axis . max as any ) ) { const radius = series . radii && series . radii [ i ] || 0 ; pxMin = Math . min ( ( ( data [ i ] - ( min as any ) ) * transA ) - radius , pxMin ) ; pxMax = Math . max ( ( ( data [ i ] - ( min as any ) ) * transA ) + radius , pxMax ) ; } } } } } ) ; if ( hasActiveSeries && range > 0 && ! this . logarithmic ) { pxMax -= axisLength ; transA *= ( axisLength + Math . max ( 0 , pxMin ) - Math . min ( pxMax , axisLength ) ) / axisLength ; ( [ [ 'min' , 'userMin' , pxMin ] , [ 'max' , 'userMax' , pxMax ] ] as Array < [ string , string , number ] > ) . forEach ( function ( keys ) : void { if ( typeof pick ( ( axis . options as any ) [ keys [ 0 ] ] , ( axis as any ) [ keys [ 1 ] ] ) === 'undefined' ) { ( axis as any ) [ keys [ 0 ] ] += keys [ 2 ] / transA ; } } ) ; } } ; declare module '../../Core/Series/SeriesType' { interface SeriesTypeRegistry { bubble : typeof BubbleSeries ; } } SeriesRegistry . registerSeriesType ( 'bubble' , BubbleSeries ) ; export default BubbleSeries ; typedef { "area" | "width" } Highcharts . BubbleSizeByValue '' ; extends series excluding product requires apioption sample sample sample sample type { Array < Array < ( number | string ) , number > | Array < ( number | string ) , number , number > | * > } extends series . line . data product apioption extends series . line . data . marker excluding product apioption type { number | null } product apioption excluding apioption '' ;