import React , { Component , cloneElement , isValidElement , createElement } from 'react' ; import classNames from 'classnames' ; import _ , { isArray , isBoolean , isNil } from 'lodash' ; import { Surface } from '../container/Surface' ; import { Layer } from '../container/Layer' ; import { Tooltip } from '../component/Tooltip' ; import { Legend } from '../component/Legend' ; import { Curve } from '../shape/Curve' ; import { Cross } from '../shape/Cross' ; import { Sector } from '../shape/Sector' ; import { Dot } from '../shape/Dot' ; import { isInRectangle , Rectangle } from '../shape/Rectangle' ; import { findAllByType , findChildByType , getDisplayName , parseChildIndex , validateWidthHeight , isChildrenEqual , renderByOrder , getReactEventByType , } from '../util/ReactUtils' ; import { CartesianAxis } from '../cartesian/CartesianAxis' ; import { Brush } from '../cartesian/Brush' ; import { getOffset , calculateChartCoordinate } from '../util/DOMUtils' ; import { getAnyElementOfObject , hasDuplicate , uniqueId , isNumber , findEntryInArray } from '../util/DataUtils' ; import { calculateActiveTickIndex , getMainColorOfGraphicItem , getBarSizeList , getBarPosition , appendOffsetOfLegend , getLegendProps , combineEventHandlers , getTicksOfAxis , getCoordinatesOfGrid , getStackedDataOfItem , parseErrorBarsOfAxis , getBandSizeOfAxis , getStackGroupsByAxisId , isCategoricalAxis , getDomainOfItemsWithSameAxis , getDomainOfStackGroups , getDomainOfDataByKey , parseSpecifiedDomain , parseDomainOfCategoryAxis , getTooltipItem , } from '../util/ChartUtils' ; import { detectReferenceElementsDomain } from '../util/DetectReferenceElementsDomain' ; import { inRangeOfSector , polarToCartesian } from '../util/PolarUtils' ; import { shallowEqual } from '../util/ShallowEqual' ; import { eventCenter , SYNC_EVENT } from '../util/Events' ; import { LayoutType , StackOffsetType , CategoricalChartOptions , Margin , CartesianViewBox , ChartOffset , BaseAxisProps , Coordinate , ChartCoordinate , TickItem , filterProps , adaptEventHandlers , GeometrySector , } from '../util/types' ; const ORIENT_MAP = { xAxis : [ 'bottom' , 'top' ] , yAxis : [ 'left' , 'right' ] , } ; const originCoordinate = { x : 0 , y : 0 } ; const isFinit = Number . isFinite ? Number . isFinite : isFinite ; const defer = typeof requestAnimationFrame === 'function' ? requestAnimationFrame : typeof setImmediate === 'function' ? setImmediate : setTimeout ; const deferClear = typeof cancelAnimationFrame === 'function' ? cancelAnimationFrame : typeof clearImmediate === 'function' ? clearImmediate : clearTimeout ; const calculateTooltipPos = ( rangeObj , layout ) : any => { if ( layout === 'horizontal' ) { return rangeObj . x ; } if ( layout === 'vertical' ) { return rangeObj . y ; } if ( layout === 'centric' ) { return rangeObj . angle ; } return rangeObj . radius ; } ; const getActiveCoordinate = ( layout , tooltipTicks , activeIndex , rangeObj ) : ChartCoordinate => { const entry = tooltipTicks . find ( ( tick ) => tick && tick . index === activeIndex ) ; if ( entry ) { if ( layout === 'horizontal' ) { return { x : entry . coordinate , y : rangeObj . y } ; } if ( layout === 'vertical' ) { return { x : rangeObj . x , y : entry . coordinate } ; } if ( layout === 'centric' ) { const angle = entry . coordinate ; const { radius } = rangeObj ; return { ... rangeObj , ... polarToCartesian ( rangeObj . cx , rangeObj . cy , radius , angle ) , angle , radius , } ; } const radius = entry . coordinate ; const { angle } = rangeObj ; return { ... rangeObj , ... polarToCartesian ( rangeObj . cx , rangeObj . cy , radius , angle ) , angle , radius , } ; } return originCoordinate ; } ; const getDisplayedData = ( data , { graphicalItems , dataStartIndex , dataEndIndex } , item ? ) : any [ ] => { const itemsData = ( graphicalItems || [ ] ) . reduce ( ( result , child ) => { const itemData = child . props . data ; if ( itemData && itemData . length ) { return [ ... result , ... itemData ] ; } return result ; } , [ ] ) ; if ( itemsData && itemsData . length > 0 ) { return itemsData ; } if ( item && item . props && item . props . data && item . props . data . length > 0 ) { return item . props . data ; } if ( data && data . length && isNumber ( dataStartIndex ) && isNumber ( dataEndIndex ) ) { return data . slice ( dataStartIndex , dataEndIndex + 1 ) ; } return [ ] ; } ; param { Object } state param { Array } chartData param { Number } activeIndex param { String } activeLabel return { Array } const getTooltipContent = ( state , chartData , activeIndex , activeLabel ? ) : any [ ] => { const { graphicalItems , tooltipAxis } = state ; const displayedData = getDisplayedData ( chartData , state ) ; if ( activeIndex < 0 || ! graphicalItems || ! graphicalItems . length || activeIndex >= displayedData . length ) { return null ; } return graphicalItems . reduce ( ( result , child ) => { const { hide } = child . props ; if ( hide ) { return result ; } const { data } = child . props ; let payload ; if ( tooltipAxis . dataKey && ! tooltipAxis . allowDuplicatedCategory ) { const entries = data === undefined ? displayedData : data ; payload = findEntryInArray ( entries , tooltipAxis . dataKey , activeLabel ) ; } else { payload = ( data && data [ activeIndex ] ) || displayedData [ activeIndex ] ; } if ( ! payload ) { return result ; } return [ ... result , getTooltipItem ( child , payload ) ] ; } , [ ] ) ; } ; param { Object } state param { Array } chartData param { String } layout param { Object } rangeObj return { Object } const getTooltipData = ( state , chartData , layout , rangeObj ? ) => { const rangeData = rangeObj || { x : state . chartX , y : state . chartY } ; const pos = calculateTooltipPos ( rangeData , layout ) ; const { orderedTooltipTicks : ticks , tooltipAxis : axis , tooltipTicks } = state ; const activeIndex = calculateActiveTickIndex ( pos , ticks , tooltipTicks , axis ) ; if ( activeIndex >= 0 && tooltipTicks ) { const activeLabel = tooltipTicks [ activeIndex ] && tooltipTicks [ activeIndex ] . value ; const activePayload = getTooltipContent ( state , chartData , activeIndex , activeLabel ) ; const activeCoordinate = getActiveCoordinate ( layout , ticks , activeIndex , rangeData ) ; return { activeTooltipIndex : activeIndex , activeLabel , activePayload , activeCoordinate , } ; } return null ; } ; param { Object } props param { Array } axes param { Array } graphicalItems param { String } axisType param { String } axisIdKey param { Object } stackGroups param { Number } dataStartIndex param { Number } dataEndIndex return { Object } const getAxisMapByAxes = ( props , { axes , graphicalItems , axisType , axisIdKey , stackGroups , dataStartIndex , dataEndIndex } ) => { const { layout , children , stackOffset } = props ; const isCategorical = isCategoricalAxis ( layout , axisType ) ; const axisMap = axes . reduce ( ( result , child ) => { const { type , dataKey , allowDataOverflow , allowDuplicatedCategory , scale , ticks } = child . props ; const axisId = child . props [ axisIdKey ] ; const displayedData = getDisplayedData ( props . data , { graphicalItems : graphicalItems . filter ( ( item ) => item . props [ axisIdKey ] === axisId ) , dataStartIndex , dataEndIndex , } ) ; const len = displayedData . length ; if ( ! result [ axisId ] ) { let domain , duplicateDomain , categoricalDomain ; if ( dataKey ) { domain = getDomainOfDataByKey ( displayedData , dataKey , type ) ; if ( type === 'category' && isCategorical ) { const duplicate = hasDuplicate ( domain ) ; if ( allowDuplicatedCategory && duplicate ) { duplicateDomain = domain ; domain = _ . range ( 0 , len ) ; } else if ( ! allowDuplicatedCategory ) { domain = parseDomainOfCategoryAxis ( child . props . domain , domain , child ) . reduce ( ( finalDomain , entry ) => finalDomain . indexOf ( entry ) >= 0 ? finalDomain : [ ... finalDomain , entry ] , [ ] ) ; } } else if ( type === 'category' ) { if ( ! allowDuplicatedCategory ) { domain = parseDomainOfCategoryAxis ( child . props . domain , domain , child ) . reduce ( ( finalDomain , entry ) => finalDomain . indexOf ( entry ) >= 0 || entry === '' || _ . isNil ( entry ) ? finalDomain : [ ... finalDomain , entry ] , [ ] ) ; } else { domain = domain . filter ( ( entry ) => entry !== '' && ! _ . isNil ( entry ) ) ; } } else if ( type === 'number' ) { const errorBarsDomain = parseErrorBarsOfAxis ( displayedData , graphicalItems . filter ( ( item ) => item . props [ axisIdKey ] === axisId && ! item . props . hide ) , dataKey , axisType ) ; if ( errorBarsDomain ) { domain = errorBarsDomain ; } } if ( isCategorical && ( type === 'number' || scale !== 'auto' ) ) { categoricalDomain = getDomainOfDataByKey ( displayedData , dataKey , 'category' ) ; } } else if ( isCategorical ) { domain = _ . range ( 0 , len ) ; } else if ( stackGroups && stackGroups [ axisId ] && stackGroups [ axisId ] . hasStack && type === 'number' ) { domain = stackOffset === 'expand' ? [ 0 , 1 ] : getDomainOfStackGroups ( stackGroups [ axisId ] . stackGroups , dataStartIndex , dataEndIndex ) ; } else { domain = getDomainOfItemsWithSameAxis ( displayedData , graphicalItems . filter ( ( item ) => item . props [ axisIdKey ] === axisId && ! item . props . hide ) , type , true ) ; } if ( type === 'number' ) { domain = detectReferenceElementsDomain ( children , domain , axisId , axisType , ticks ) ; if ( child . props . domain ) { domain = parseSpecifiedDomain ( child . props . domain , domain , allowDataOverflow ) ; } } else if ( type === 'category' && child . props . domain ) { const axisDomain = child . props . domain ; const isDomainValidate = domain . every ( ( entry ) => axisDomain . indexOf ( entry ) >= 0 ) ; if ( isDomainValidate ) { domain = axisDomain ; } } return { ... result , [ axisId ] : { ... child . props , axisType , domain , categoricalDomain , duplicateDomain , originalDomain : child . props . domain , isCategorical , layout , } , } ; } return result ; } , { } ) ; return axisMap ; } ; param { Object } props param { Array } graphicalItems param { ReactElement } Axis param { String } axisType param { String } axisIdKey param { Object } stackGroups param { Number } dataStartIndex param { Number } dataEndIndex return { Object } const getAxisMapByItems = ( props , { graphicalItems , Axis , axisType , axisIdKey , stackGroups , dataStartIndex , dataEndIndex } ) => { const { layout , children } = props ; const displayedData = getDisplayedData ( props . data , { graphicalItems , dataStartIndex , dataEndIndex , } ) ; const len = displayedData . length ; const isCategorical = isCategoricalAxis ( layout , axisType ) ; let index = - 1 ; const axisMap = graphicalItems . reduce ( ( result , child ) => { const axisId = child . props [ axisIdKey ] ; if ( ! result [ axisId ] ) { index ++ ; let domain ; if ( isCategorical ) { domain = _ . range ( 0 , len ) ; } else if ( stackGroups && stackGroups [ axisId ] && stackGroups [ axisId ] . hasStack ) { domain = getDomainOfStackGroups ( stackGroups [ axisId ] . stackGroups , dataStartIndex , dataEndIndex ) ; domain = detectReferenceElementsDomain ( children , domain , axisId , axisType ) ; } else { domain = parseSpecifiedDomain ( Axis . defaultProps . domain , getDomainOfItemsWithSameAxis ( displayedData , graphicalItems . filter ( ( item ) => item . props [ axisIdKey ] === axisId && ! item . props . hide ) , 'number' ) , Axis . defaultProps . allowDataOverflow ) ; domain = detectReferenceElementsDomain ( children , domain , axisId , axisType ) ; } return { ... result , [ axisId ] : { axisType , ... Axis . defaultProps , hide : true , orientation : _ . get ( ORIENT_MAP , `${ axisType }.${ index % 2 }` , null ) , domain , originalDomain : Axis . defaultProps . domain , isCategorical , layout , } , } ; } return result ; } , { } ) ; return axisMap ; } ; param { Object } props param { String } axisType param { Array } graphicalItems param { Object } stackGroups param { Number } dataStartIndex param { Number } dataEndIndex return { Object } const getAxisMap = ( props , { axisType = 'xAxis' , AxisComp , graphicalItems , stackGroups , dataStartIndex , dataEndIndex } ) => { const { children } = props ; const axisIdKey = `${ axisType }Id` ; const axes = findAllByType ( children , AxisComp ) ; let axisMap = { } ; if ( axes && axes . length ) { axisMap = getAxisMapByAxes ( props , { axes , graphicalItems , axisType , axisIdKey , stackGroups , dataStartIndex , dataEndIndex , } ) ; } else if ( graphicalItems && graphicalItems . length ) { axisMap = getAxisMapByItems ( props , { Axis : AxisComp , graphicalItems , axisType , axisIdKey , stackGroups , dataStartIndex , dataEndIndex , } ) ; } return axisMap ; } ; const tooltipTicksGenerator = ( axisMap ) => { const axis = getAnyElementOfObject ( axisMap ) ; const tooltipTicks = getTicksOfAxis ( axis , false , true ) ; return { tooltipTicks , orderedTooltipTicks : _ . sortBy ( tooltipTicks , o => o . coordinate ) , tooltipAxis : axis , tooltipAxisBandSize : getBandSizeOfAxis ( axis , tooltipTicks ) , } ; } ; param { Object } props return { Object } const createDefaultState = ( props ) : CategoricalChartState => { const { children , defaultShowTooltip } = props ; const brushItem = findChildByType ( children , Brush . displayName ) ; const startIndex = ( brushItem && brushItem . props && brushItem . props . startIndex ) || 0 ; const endIndex = ( brushItem && brushItem . props && brushItem . props . endIndex ) || ( props . data && props . data . length - 1 ) || 0 ; return { chartX : 0 , chartY : 0 , dataStartIndex : startIndex , dataEndIndex : endIndex , activeTooltipIndex : - 1 , isTooltipActive : ! _ . isNil ( defaultShowTooltip ) ? defaultShowTooltip : false , } ; } ; const hasGraphicalBarItem = ( graphicalItems ) : any [ ] | boolean => { if ( ! graphicalItems || ! graphicalItems . length ) { return false ; } return graphicalItems . some ( ( item ) => { const name = getDisplayName ( item && item . type ) ; return name && name . indexOf ( 'Bar' ) >= 0 ; } ) ; } ; const getAxisNameByLayout = ( layout ) => { if ( layout === 'horizontal' ) { return { numericAxisName : 'yAxis' , cateAxisName : 'xAxis' } ; } if ( layout === 'vertical' ) { return { numericAxisName : 'xAxis' , cateAxisName : 'yAxis' } ; } if ( layout === 'centric' ) { return { numericAxisName : 'radiusAxis' , cateAxisName : 'angleAxis' } ; } return { numericAxisName : 'angleAxis' , cateAxisName : 'radiusAxis' } ; } ; param { Object } props param { Object } prevLegendBBox return { Object } const calculateOffset = ( { props , graphicalItems , xAxisMap = { } as BaseAxisProps , yAxisMap = { } as BaseAxisProps } , prevLegendBBox ? ) => { const { width , height , children } = props ; const margin = props . margin || { } ; const brushItem = findChildByType ( children , Brush . displayName ) ; const legendItem = findChildByType ( children , Legend . displayName ) ; const offsetH = Object . keys ( yAxisMap ) . reduce ( ( result , id ) => { const entry = yAxisMap [ id ] ; const { orientation } = entry ; if ( ! entry . mirror && ! entry . hide ) { return { ... result , [ orientation ] : result [ orientation ] + entry . width } ; } return result ; } , { left : margin . left || 0 , right : margin . right || 0 } ) ; const offsetV = Object . keys ( xAxisMap ) . reduce ( ( result , id ) => { const entry = xAxisMap [ id ] ; const { orientation } = entry ; if ( ! entry . mirror && ! entry . hide ) { return { ... result , [ orientation ] : _ . get ( result , `${ orientation }` ) + entry . height } ; } return result ; } , { top : margin . top || 0 , bottom : margin . bottom || 0 } ) ; let offset = { ... offsetV , ... offsetH } ; const brushBottom = offset . bottom ; if ( brushItem ) { offset . bottom += brushItem . props . height || Brush . defaultProps . height ; } if ( legendItem && prevLegendBBox ) { offset = appendOffsetOfLegend ( offset , graphicalItems , props , prevLegendBBox ) ; } return { brushBottom , ... offset , width : width - offset . left - offset . right , height : height - offset . top - offset . bottom , } ; } ; export interface CategoricalChartState { chartX ? : number ; chartY ? : number ; dataStartIndex ? : number ; dataEndIndex ? : number ; activeTooltipIndex ? : number ; isTooltipActive ? : boolean ; updateId ? : number ; xAxisMap ? : { [ k ] : BaseAxisProps ; } ; yAxisMap ? : { [ k ] : BaseAxisProps ; } ; orderedTooltipTicks ? : any ; tooltipAxis ? : BaseAxisProps ; tooltipTicks ? : TickItem [ ] ; graphicalItems ? : any ; activeCoordinate ? : ChartCoordinate ; offset ? : ChartOffset ; angleAxisMap ? : any ; radiusAxisMap ? : any ; formattedGraphicalItems ? : any ; /** active tooltip payload */ activePayload ? : any [ ] ; tooltipAxisBandSize ? : number ; /** active item */ activeItem ? : any ; /** Active label of data */ activeLabel ? : string ; xValue ? : number ; yValue ? : number ; legendBBox ? : any ; prevData ? : any [ ] ; prevWidth ? : number ; prevHeight ? : number ; prevLayout ? : LayoutType ; prevStackOffset ? : StackOffsetType ; prevMargin ? : Margin ; prevChildren ? : any ; } export type CategoricalChartFunc = ( nextState : CategoricalChartState , event : any ) => void ; export interface CategoricalChartProps { syncId ? : number | string ; syncMethod ? : 'index' | 'value' | Function ; compact ? : boolean ; width ? : number ; height ? : number ; data ? : any [ ] ; layout ? : LayoutType ; stackOffset ? : StackOffsetType ; throttleDelay ? : number ; margin ? : Margin ; barCategoryGap ? : number | string ; barGap ? : number | string ; barSize ? : number | string ; maxBarSize ? : number ; style ? : any ; className ? : string ; children ? : any ; defaultShowTooltip ? : boolean ; onClick ? : CategoricalChartFunc ; onMouseLeave ? : CategoricalChartFunc ; onMouseEnter ? : CategoricalChartFunc ; onMouseMove ? : CategoricalChartFunc ; onMouseDown ? : CategoricalChartFunc ; onMouseUp ? : CategoricalChartFunc ; reverseStackOrder ? : boolean ; id ? : string ; startAngle ? : number ; endAngle ? : number ; cx ? : number | string ; cy ? : number | string ; innerRadius ? : number | string ; outerRadius ? : number | string ; } export const generateCategoricalChart = ( { chartName , GraphicalChild , defaultTooltipEventType = 'axis' , validateTooltipEventTypes = [ 'axis' ] , axisComponents , legendContent , formatAxisMap , defaultProps , } ) => { const getFormatItems = ( props , currentState ) : any [ ] => { const { graphicalItems , stackGroups , offset , updateId , dataStartIndex , dataEndIndex } = currentState ; const { barSize , layout , barGap , barCategoryGap , maxBarSize : globalMaxBarSize } = props ; const { numericAxisName , cateAxisName } = getAxisNameByLayout ( layout ) ; const hasBar = hasGraphicalBarItem ( graphicalItems ) ; const sizeList = hasBar && getBarSizeList ( { barSize , stackGroups } ) ; const formattedItems = [ ] as any [ ] ; graphicalItems . forEach ( ( item , index ) => { const displayedData = getDisplayedData ( props . data , { dataStartIndex , dataEndIndex } , item ) ; const { dataKey , maxBarSize : childMaxBarSize } = item . props ; const numericAxisId = item . props [ `${ numericAxisName }Id` ] ; const cateAxisId = item . props [ `${ cateAxisName }Id` ] ; const axisObj = axisComponents . reduce ( ( result , entry ) => { const axisMap = currentState [ `${ entry . axisType }Map` ] ; const id = item . props [ `${ entry . axisType }Id` ] ; const axis = axisMap && axisMap [ id ] ; return { ... result , [ entry . axisType ] : axis , [ `${ entry . axisType }Ticks` ] : getTicksOfAxis ( axis ) , } ; } , { } ) ; const cateAxis = axisObj [ cateAxisName ] ; const cateTicks = axisObj [ `${ cateAxisName }Ticks` ] ; const stackedData = stackGroups && stackGroups [ numericAxisId ] && stackGroups [ numericAxisId ] . hasStack && getStackedDataOfItem ( item , stackGroups [ numericAxisId ] . stackGroups ) ; const itemIsBar = getDisplayName ( item . type ) . indexOf ( 'Bar' ) >= 0 ; const bandSize = getBandSizeOfAxis ( cateAxis , cateTicks ) ; let barPosition = [ ] ; if ( itemIsBar ) { const maxBarSize = _ . isNil ( childMaxBarSize ) ? globalMaxBarSize : childMaxBarSize ; const barBandSize = getBandSizeOfAxis ( cateAxis , cateTicks , true ) ?? maxBarSize ?? 0 ; barPosition = getBarPosition ( { barGap , barCategoryGap , bandSize : barBandSize !== bandSize ? barBandSize : bandSize , sizeList : sizeList [ cateAxisId ] , maxBarSize , } ) ; if ( barBandSize !== bandSize ) { barPosition = barPosition . map ( ( pos ) => ( { ... pos , position : { ... pos . position , offset : pos . position . offset - barBandSize / 2 } , } ) ) ; } } const composedFn = item && item . type && item . type . getComposedData ; if ( composedFn ) { formattedItems . push ( { props : { ... composedFn ( { ... axisObj , displayedData , props , dataKey , item , bandSize , barPosition , offset , stackedData , layout , dataStartIndex , dataEndIndex , } ) , key : item . key || `item-${ index }` , [ numericAxisName ] : axisObj [ numericAxisName ] , [ cateAxisName ] : axisObj [ cateAxisName ] , animationId : updateId , } , childIndex : parseChildIndex ( item , props . children ) , item , } ) ; } } ) ; return formattedItems ; } ; param { Object } props param { Object } prevState return { Object } const updateStateOfAxisMapsOffsetAndStackGroups = ( { props , dataStartIndex , dataEndIndex , updateId } , prevState ? ) : any => { if ( ! validateWidthHeight ( { props } ) ) { return null ; } const { children , layout , stackOffset , data , reverseStackOrder } = props ; const { numericAxisName , cateAxisName } = getAxisNameByLayout ( layout ) ; const graphicalItems = findAllByType ( children , GraphicalChild ) ; const stackGroups = getStackGroupsByAxisId ( data , graphicalItems , `${ numericAxisName }Id` , `${ cateAxisName }Id` , stackOffset , reverseStackOrder ) ; const axisObj = axisComponents . reduce ( ( result , entry ) => { const name = `${ entry . axisType }Map` ; return { ... result , [ name ] : getAxisMap ( props , { ... entry , graphicalItems , stackGroups : entry . axisType === numericAxisName && stackGroups , dataStartIndex , dataEndIndex , } ) , } ; } , { } ) ; const offset = calculateOffset ( { ... axisObj , props , graphicalItems } , prevState ?. legendBBox ) ; Object . keys ( axisObj ) . forEach ( key => { axisObj [ key ] = formatAxisMap ( props , axisObj [ key ] , offset , key . replace ( 'Map' , '' ) , chartName ) ; } ) ; const cateAxisMap = axisObj [ `${ cateAxisName }Map` ] ; const ticksObj = tooltipTicksGenerator ( cateAxisMap ) ; const formattedGraphicalItems = getFormatItems ( props , { ... axisObj , dataStartIndex , dataEndIndex , updateId , graphicalItems , stackGroups , offset , } ) ; return { formattedGraphicalItems , graphicalItems , offset , stackGroups , ... ticksObj , ... axisObj , } ; } ; return class CategoricalChartWrapper extends Component < CategoricalChartProps , CategoricalChartState > { static displayName = chartName ; uniqueChartId : any ; clipPathId : any ; legendInstance : any ; deferId : any ; static defaultProps : CategoricalChartProps = { layout : 'horizontal' , stackOffset : 'none' , barCategoryGap : '10%' , barGap : 4 , margin : { top : 5 , right : 5 , bottom : 5 , left : 5 } as Margin , reverseStackOrder : false , syncMethod : 'index' , ... defaultProps , } ; container ? : any ; constructor ( props ) { super ( props ) ; this . uniqueChartId = _ . isNil ( props . id ) ? uniqueId ( 'recharts' ) : props . id ; this . clipPathId = `${ this . uniqueChartId }-clip` ; if ( props . throttleDelay ) { this . triggeredAfterMouseMove = _ . throttle ( this . triggeredAfterMouseMove , props . throttleDelay ) ; } this . state = { } ; } componentDidMount ( ) { if ( ! _ . isNil ( this . props . syncId ) ) { this . addListener ( ) ; } } static getDerivedStateFromProps = ( nextProps , prevState ) : CategoricalChartState => { const { data , children , width , height , layout , stackOffset , margin } = nextProps ; if ( _ . isNil ( prevState . updateId ) ) { const defaultState = createDefaultState ( nextProps ) ; return { ... defaultState , updateId : 0 , ... updateStateOfAxisMapsOffsetAndStackGroups ( { props : nextProps , ... defaultState , updateId : 0 , } , prevState ) , prevData : data , prevWidth : width , prevHeight : height , prevLayout : layout , prevStackOffset : stackOffset , prevMargin : margin , prevChildren : children , } ; } if ( data !== prevState . prevData || width !== prevState . prevWidth || height !== prevState . prevHeight || layout !== prevState . prevLayout || stackOffset !== prevState . prevStackOffset || ! shallowEqual ( margin , prevState . prevMargin ) ) { const defaultState = createDefaultState ( nextProps ) ; const keepFromPrevState = { chartX : prevState . chartX , chartY : prevState . chartY , isTooltipActive : prevState . isTooltipActive , } ; const updatesToState = { ... getTooltipData ( prevState , data , layout ) , updateId : prevState . updateId + 1 , } ; const newState = { ... defaultState , ... keepFromPrevState , ... updatesToState , } ; return { ... newState , ... updateStateOfAxisMapsOffsetAndStackGroups ( { props : nextProps , ... newState , } , prevState ) , prevData : data , prevWidth : width , prevHeight : height , prevLayout : layout , prevStackOffset : stackOffset , prevMargin : margin , prevChildren : children , } ; } if ( ! isChildrenEqual ( children , prevState . prevChildren ) ) { const hasGlobalData = ! _ . isNil ( data ) ; const newUpdateId = hasGlobalData ? prevState . updateId : prevState . updateId + 1 ; return { updateId : newUpdateId , ... updateStateOfAxisMapsOffsetAndStackGroups ( { props : nextProps , ... prevState , updateId : newUpdateId , } , prevState ) , prevChildren : children , } ; } return null ; } ; componentDidUpdate ( prevProps ) { if ( _ . isNil ( prevProps . syncId ) && ! _ . isNil ( this . props . syncId ) ) { this . addListener ( ) ; } if ( ! _ . isNil ( prevProps . syncId ) && _ . isNil ( this . props . syncId ) ) { this . removeListener ( ) ; } } componentWillUnmount ( ) { this . clearDeferId ( ) ; if ( ! _ . isNil ( this . props . syncId ) ) { this . removeListener ( ) ; } this . cancelThrottledTriggerAfterMouseMove ( ) ; } cancelThrottledTriggerAfterMouseMove ( ) { if ( typeof ( this . triggeredAfterMouseMove as any ) . cancel === 'function' ) { ( this . triggeredAfterMouseMove as any ) . cancel ( ) ; } } getTooltipEventType ( ) { const tooltipItem = findChildByType ( this . props . children , Tooltip . displayName ) ; if ( tooltipItem && isBoolean ( tooltipItem . props . shared ) ) { const eventType = tooltipItem . props . shared ? 'axis' : 'item' ; return validateTooltipEventTypes . indexOf ( eventType ) >= 0 ? eventType : defaultTooltipEventType ; } return defaultTooltipEventType ; } param { Object } event return { Object } getMouseInfo ( event ) { if ( ! this . container ) { return null ; } const containerOffset = getOffset ( this . container ) ; const e = calculateChartCoordinate ( event , containerOffset ) ; const rangeObj = this . inRange ( e . chartX , e . chartY ) ; if ( ! rangeObj ) { return null ; } const { xAxisMap , yAxisMap } = this . state ; const tooltipEventType = this . getTooltipEventType ( ) ; if ( tooltipEventType !== 'axis' && xAxisMap && yAxisMap ) { const xScale = getAnyElementOfObject ( xAxisMap ) . scale ; const yScale = getAnyElementOfObject ( yAxisMap ) . scale ; const xValue = xScale && xScale . invert ? xScale . invert ( e . chartX ) : null ; const yValue = yScale && yScale . invert ? yScale . invert ( e . chartY ) : null ; return { ... e , xValue , yValue } ; } const toolTipData = getTooltipData ( this . state , this . props . data , this . props . layout , rangeObj ) ; if ( toolTipData ) { return { ... e , ... toolTipData , } ; } return null ; } getCursorRectangle ( ) { const { layout } = this . props ; const { activeCoordinate , offset , tooltipAxisBandSize } = this . state ; const halfSize = tooltipAxisBandSize / 2 ; return { stroke : 'none' , fill : '#ccc' , x : layout === 'horizontal' ? activeCoordinate . x - halfSize : offset . left + 0.5 , y : layout === 'horizontal' ? offset . top + 0.5 : activeCoordinate . y - halfSize , width : layout === 'horizontal' ? tooltipAxisBandSize : offset . width - 1 , height : layout === 'horizontal' ? offset . height - 1 : tooltipAxisBandSize , } ; } getCursorPoints ( ) { const { layout } = this . props ; const { activeCoordinate , offset } = this . state ; let x1 , y1 , x2 , y2 ; if ( layout === 'horizontal' ) { x1 = activeCoordinate . x ; x2 = x1 ; y1 = offset . top ; y2 = offset . top + offset . height ; } else if ( layout === 'vertical' ) { y1 = activeCoordinate . y ; y2 = y1 ; x1 = offset . left ; x2 = offset . left + offset . width ; } else if ( ! _ . isNil ( activeCoordinate . cx ) || ! _ . isNil ( activeCoordinate . cy ) ) { if ( layout === 'centric' ) { const { cx , cy , innerRadius , outerRadius , angle } = activeCoordinate ; const innerPoint = polarToCartesian ( cx , cy , innerRadius , angle ) ; const outerPoint = polarToCartesian ( cx , cy , outerRadius , angle ) ; x1 = innerPoint . x ; y1 = innerPoint . y ; x2 = outerPoint . x ; y2 = outerPoint . y ; } else { const { cx , cy , radius , startAngle , endAngle } = activeCoordinate ; const startPoint = polarToCartesian ( cx , cy , radius , startAngle ) ; const endPoint = polarToCartesian ( cx , cy , radius , endAngle ) ; return { points : [ startPoint , endPoint ] , cx , cy , radius , startAngle , endAngle , } ; } } return [ { x : x1 , y : y1 } , { x : x2 , y : y2 } , ] ; } inRange ( x , y ) { const { layout } = this . props ; if ( layout === 'horizontal' || layout === 'vertical' ) { const { offset } = this . state ; const isInRange = x >= offset . left && x <= offset . left + offset . width && y >= offset . top && y <= offset . top + offset . height ; return isInRange ? { x , y } : null ; } const { angleAxisMap , radiusAxisMap } = this . state ; if ( angleAxisMap && radiusAxisMap ) { const angleAxis = getAnyElementOfObject ( angleAxisMap ) ; return inRangeOfSector ( { x , y } , angleAxis ) ; } return null ; } parseEventsOfWrapper ( ) { const { children } = this . props ; const tooltipEventType = this . getTooltipEventType ( ) ; const tooltipItem = findChildByType ( children , Tooltip . displayName ) ; let tooltipEvents = { } ; if ( tooltipItem && tooltipEventType === 'axis' ) { if ( tooltipItem . props . trigger === 'click' ) { tooltipEvents = { onClick : this . handleClick , } ; } else { tooltipEvents = { onMouseEnter : this . handleMouseEnter , onMouseMove : this . handleMouseMove , onMouseLeave : this . handleMouseLeave , onTouchMove : this . handleTouchMove , onTouchStart : this . handleTouchStart , onTouchEnd : this . handleTouchEnd , } ; } } const outerEvents = adaptEventHandlers ( this . props , this . handleOuterEvent ) ; return { ... outerEvents , ... tooltipEvents , } ; } addListener ( ) { eventCenter . on ( SYNC_EVENT , this . handleReceiveSyncEvent ) ; if ( eventCenter . setMaxListeners && eventCenter . _maxListeners ) { eventCenter . setMaxListeners ( eventCenter . _maxListeners + 1 ) ; } } removeListener ( ) { eventCenter . removeListener ( SYNC_EVENT , this . handleReceiveSyncEvent ) ; if ( eventCenter . setMaxListeners && eventCenter . _maxListeners ) { eventCenter . setMaxListeners ( eventCenter . _maxListeners - 1 ) ; } } clearDeferId = ( ) => { if ( ! isNil ( this . deferId ) && deferClear ) { deferClear ( this . deferId ) ; } this . deferId = null ; } ; handleLegendBBoxUpdate = ( box ) => { if ( box && this . legendInstance ) { const { dataStartIndex , dataEndIndex , updateId } = this . state ; this . setState ( { legendBBox : box , ... updateStateOfAxisMapsOffsetAndStackGroups ( { props : this . props , dataStartIndex , dataEndIndex , updateId , } , { ... this . state , legendBBox : box } ) , } ) ; } } ; handleReceiveSyncEvent = ( cId , chartId , data ) => { const { syncId } = this . props ; if ( syncId === cId && chartId !== this . uniqueChartId ) { this . clearDeferId ( ) ; this . deferId = defer && defer ( this . applySyncEvent . bind ( this , data ) ) ; } } ; handleBrushChange = ( { startIndex , endIndex } ) => { if ( startIndex !== this . state . dataStartIndex || endIndex !== this . state . dataEndIndex ) { const { updateId } = this . state ; this . setState ( ( ) => ( { dataStartIndex : startIndex , dataEndIndex : endIndex , ... updateStateOfAxisMapsOffsetAndStackGroups ( { props : this . props , dataStartIndex : startIndex , dataEndIndex : endIndex , updateId , } , this . state ) , } ) ) ; this . triggerSyncEvent ( { dataStartIndex : startIndex , dataEndIndex : endIndex , } ) ; } } ; param { Object } e return { Null } handleMouseEnter = ( e ) => { const { onMouseEnter } = this . props ; const mouse = this . getMouseInfo ( e ) ; if ( mouse ) { const nextState = { ... mouse , isTooltipActive : true } ; this . setState ( nextState ) ; this . triggerSyncEvent ( nextState ) ; if ( _ . isFunction ( onMouseEnter ) ) { onMouseEnter ( nextState , e ) ; } } } ; triggeredAfterMouseMove = ( e ) : any => { const { onMouseMove } = this . props ; const mouse = this . getMouseInfo ( e ) ; const nextState = mouse ? { ... mouse , isTooltipActive : true } : { isTooltipActive : false } ; this . setState ( nextState ) ; this . triggerSyncEvent ( nextState ) ; if ( _ . isFunction ( onMouseMove ) ) { onMouseMove ( nextState , e ) ; } } ; param { Object } el return { Object } handleItemMouseEnter = ( el ) => { this . setState ( ( ) => ( { isTooltipActive : true , activeItem : el , activePayload : el . tooltipPayload , activeCoordinate : el . tooltipPosition || { x : el . cx , y : el . cy } , } ) ) ; } ; return { Object } handleItemMouseLeave = ( ) => { this . setState ( ( ) => ( { isTooltipActive : false , } ) ) ; } ; param { Object } e return { Null } handleMouseMove = ( e ) => { if ( e && _ . isFunction ( e . persist ) ) { e . persist ( ) ; } this . triggeredAfterMouseMove ( e ) ; } ; param { Object } e return { Null } handleMouseLeave = ( e ) => { const { onMouseLeave } = this . props ; const nextState = { isTooltipActive : false } ; this . setState ( nextState ) ; this . triggerSyncEvent ( nextState ) ; if ( _ . isFunction ( onMouseLeave ) ) { onMouseLeave ( nextState , e ) ; } this . cancelThrottledTriggerAfterMouseMove ( ) ; } ; handleOuterEvent = ( e ) => { const eventName = getReactEventByType ( e ) ; const event = _ . get ( this . props , `${ eventName }` ) ; if ( eventName && _ . isFunction ( event ) ) { let mouse ; if ( /.*touch.*/i . test ( eventName ) ) { mouse = this . getMouseInfo ( e . changedTouches [ 0 ] ) ; } else { mouse = this . getMouseInfo ( e ) ; } const handler = event ; handler ( mouse , e ) ; } } ; handleClick = ( e ) => { const { onClick } = this . props ; const mouse = this . getMouseInfo ( e ) ; if ( mouse ) { const nextState = { ... mouse , isTooltipActive : true } ; this . setState ( nextState ) ; this . triggerSyncEvent ( nextState ) ; if ( _ . isFunction ( onClick ) ) { onClick ( nextState , e ) ; } } } ; handleMouseDown = ( e ) => { const { onMouseDown } = this . props ; if ( _ . isFunction ( onMouseDown ) ) { const nextState = this . getMouseInfo ( e ) ; onMouseDown ( nextState , e ) ; } } ; handleMouseUp = ( e ) => { const { onMouseUp } = this . props ; if ( _ . isFunction ( onMouseUp ) ) { const nextState = this . getMouseInfo ( e ) ; onMouseUp ( nextState , e ) ; } } ; handleTouchMove = ( e ) => { if ( e . changedTouches != null && e . changedTouches . length > 0 ) { this . handleMouseMove ( e . changedTouches [ 0 ] ) ; } } ; handleTouchStart = ( e ) => { if ( e . changedTouches != null && e . changedTouches . length > 0 ) { this . handleMouseDown ( e . changedTouches [ 0 ] ) ; } } ; handleTouchEnd = ( e ) => { if ( e . changedTouches != null && e . changedTouches . length > 0 ) { this . handleMouseUp ( e . changedTouches [ 0 ] ) ; } } ; triggerSyncEvent ( data ) { const { syncId } = this . props ; if ( ! _ . isNil ( syncId ) ) { eventCenter . emit ( SYNC_EVENT , syncId , this . uniqueChartId , data ) ; } } applySyncEvent ( data ) { const { layout , syncMethod } = this . props ; const { updateId } = this . state ; const { dataStartIndex , dataEndIndex } = data ; if ( ! _ . isNil ( data . dataStartIndex ) || ! _ . isNil ( data . dataEndIndex ) ) { this . setState ( { dataStartIndex , dataEndIndex , ... updateStateOfAxisMapsOffsetAndStackGroups ( { props : this . props , dataStartIndex , dataEndIndex , updateId , } , this . state ) , } ) ; } else if ( ! _ . isNil ( data . activeTooltipIndex ) ) { const { chartX , chartY } = data ; let { activeTooltipIndex } = data ; const { offset , tooltipTicks } = this . state ; if ( ! offset ) { return ; } if ( typeof syncMethod === 'function' ) { activeTooltipIndex = syncMethod ( tooltipTicks , data ) ; } else if ( syncMethod === 'value' ) { activeTooltipIndex = - 1 ; for ( let i = 0 ; i < tooltipTicks . length ; i ++ ) { if ( tooltipTicks [ i ] . value === data . activeLabel ) { activeTooltipIndex = i ; break ; } } } const viewBox = { ... offset , x : offset . left , y : offset . top } ; const validateChartX = Math . min ( chartX , viewBox . x + viewBox . width ) ; const validateChartY = Math . min ( chartY , viewBox . y + viewBox . height ) ; const activeLabel = tooltipTicks [ activeTooltipIndex ] && tooltipTicks [ activeTooltipIndex ] . value ; const activePayload = getTooltipContent ( this . state , this . props . data , activeTooltipIndex ) ; const activeCoordinate = tooltipTicks [ activeTooltipIndex ] ? { x : layout === 'horizontal' ? tooltipTicks [ activeTooltipIndex ] . coordinate : validateChartX , y : layout === 'horizontal' ? validateChartY : tooltipTicks [ activeTooltipIndex ] . coordinate , } : originCoordinate ; this . setState ( { ... data , activeLabel , activeCoordinate , activePayload , activeTooltipIndex , } ) ; } else { this . setState ( data ) ; } } verticalCoordinatesGenerator = ( { xAxis , width , height , offset } ) => getCoordinatesOfGrid ( CartesianAxis . getTicks ( { ... CartesianAxis . defaultProps , ... xAxis , ticks : getTicksOfAxis ( xAxis , true ) , viewBox : { x : 0 , y : 0 , width , height } , } ) , offset . left , offset . left + offset . width ) ; horizontalCoordinatesGenerator = ( { yAxis , width , height , offset } ) => getCoordinatesOfGrid ( CartesianAxis . getTicks ( { ... CartesianAxis . defaultProps , ... yAxis , ticks : getTicksOfAxis ( yAxis , true ) , viewBox : { x : 0 , y : 0 , width , height } , } ) , offset . top , offset . top + offset . height ) ; axesTicksGenerator = ( axis ? ) => getTicksOfAxis ( axis , true ) ; filterFormatItem ( item , displayName , childIndex ) { const { formattedGraphicalItems } = this . state ; for ( let i = 0 , len = formattedGraphicalItems . length ; i < len ; i ++ ) { const entry = formattedGraphicalItems [ i ] ; if ( entry . item === item || entry . props . key === item . key || ( displayName === getDisplayName ( entry . item . type ) && childIndex === entry . childIndex ) ) { return entry ; } } return null ; } renderCursor = ( element ) => { const { isTooltipActive , activeCoordinate , activePayload , offset , activeTooltipIndex } = this . state ; const tooltipEventType = this . getTooltipEventType ( ) ; if ( ! element || ! element . props . cursor || ! isTooltipActive || ! activeCoordinate || ( chartName !== 'ScatterChart' && tooltipEventType !== 'axis' ) ) { return null ; } const { layout } = this . props ; let restProps ; let cursorComp = Curve ; if ( chartName === 'ScatterChart' ) { restProps = activeCoordinate ; cursorComp = Cross ; } else if ( chartName === 'BarChart' ) { restProps = this . getCursorRectangle ( ) ; cursorComp = Rectangle ; } else if ( layout === 'radial' ) { const { cx , cy , radius , startAngle , endAngle } = this . getCursorPoints ( ) ; restProps = { cx , cy , startAngle , endAngle , innerRadius : radius , outerRadius : radius , } ; cursorComp = Sector ; } else { restProps = { points : this . getCursorPoints ( ) } ; cursorComp = Curve ; } const key = element . key || '_recharts-cursor' ; const cursorProps = { stroke : '#ccc' , pointerEvents : 'none' , ... offset , ... restProps , ... filterProps ( element . props . cursor ) , payload : activePayload , payloadIndex : activeTooltipIndex , key , className : 'recharts-tooltip-cursor' , } ; return isValidElement ( element . props . cursor ) ? cloneElement ( element . props . cursor , cursorProps ) : createElement ( cursorComp , cursorProps ) ; } ; renderPolarAxis = ( element , displayName , index ) => { const axisType = _ . get ( element , 'type.axisType' ) ; const axisMap = _ . get ( this . state , `${ axisType }Map` ) ; const axisOption = axisMap [ element . props [ `${ axisType }Id` ] ] ; return cloneElement ( element , { ... axisOption , className : axisType , key : element . key || `${ displayName }-${ index }` , ticks : getTicksOfAxis ( axisOption , true ) , } ) ; } ; renderXAxis = ( element , displayName , index ) => { const { xAxisMap } = this . state ; const axisObj = xAxisMap [ element . props . xAxisId ] ; return this . renderAxis ( axisObj , element , displayName , index ) ; } ; renderYAxis = ( element , displayName , index ) => { const { yAxisMap } = this . state ; const axisObj = yAxisMap [ element . props . yAxisId ] ; return this . renderAxis ( axisObj , element , displayName , index ) ; } ; param { Object } axisOptions param { Object } element param { String } displayName param { Number } index return { ReactElement } renderAxis ( axisOptions , element , displayName , index ) { const { width , height } = this . props ; return ( < CartesianAxis { ... ( axisOptions as any ) } className = { `recharts-${ axisOptions . axisType } ${ axisOptions . axisType }` } key = { element . key || `${ displayName }-${ index }` } viewBox = { { x : 0 , y : 0 , width , height } as any } ticksGenerator = { this . axesTicksGenerator } / > ) ; } param { ReactElement } element return { ReactElement } renderGrid = ( element ) : React . ReactElement => { const { xAxisMap , yAxisMap , offset } = this . state ; const { width , height } = this . props ; const xAxis = getAnyElementOfObject ( xAxisMap ) ; const yAxisWithFiniteDomain = _ . find ( yAxisMap , axis => _ . every ( axis . domain , isFinit ) ) ; const yAxis = yAxisWithFiniteDomain || getAnyElementOfObject ( yAxisMap ) ; const props = element . props || { } ; return cloneElement ( element , { key : element . key || 'grid' , x : isNumber ( props . x ) ? props . x : offset . left , y : isNumber ( props . y ) ? props . y : offset . top , width : isNumber ( props . width ) ? props . width : offset . width , height : isNumber ( props . height ) ? props . height : offset . height , xAxis , yAxis , offset , chartWidth : width , chartHeight : height , verticalCoordinatesGenerator : props . verticalCoordinatesGenerator || this . verticalCoordinatesGenerator , horizontalCoordinatesGenerator : props . horizontalCoordinatesGenerator || this . horizontalCoordinatesGenerator , } ) ; } ; renderPolarGrid = ( element ) : React . ReactElement => { const { radialLines , polarAngles , polarRadius } = element . props ; const { radiusAxisMap , angleAxisMap } = this . state ; const radiusAxis = getAnyElementOfObject ( radiusAxisMap ) ; const angleAxis = getAnyElementOfObject ( angleAxisMap ) ; const { cx , cy , innerRadius , outerRadius } = angleAxis ; return cloneElement ( element , { polarAngles : isArray ( polarAngles ) ? polarAngles : getTicksOfAxis ( angleAxis , true ) . map ( ( entry ) => entry . coordinate ) , polarRadius : isArray ( polarRadius ) ? polarRadius : getTicksOfAxis ( radiusAxis , true ) . map ( ( entry ) => entry . coordinate ) , cx , cy , innerRadius , outerRadius , key : element . key || 'polar-grid' , radialLines , } ) ; } ; return { ReactElement } renderLegend = ( ) : React . ReactElement => { const { formattedGraphicalItems } = this . state ; const { children , width , height } = this . props ; const margin = this . props . margin || { } ; const legendWidth = width - ( margin . left || 0 ) - ( margin . right || 0 ) ; const props = getLegendProps ( { children , formattedGraphicalItems , legendWidth , legendContent , } ) ; if ( ! props ) { return null ; } const { item , ... otherProps } = props ; return cloneElement ( item , { ... otherProps , chartWidth : width , chartHeight : height , margin , ref : ( legend ) => { this . legendInstance = legend ; } , onBBoxUpdate : this . handleLegendBBoxUpdate , } ) ; } ; return { ReactElement } renderTooltip = ( ) : React . ReactElement => { const { children } = this . props ; const tooltipItem = findChildByType ( children , Tooltip . displayName ) ; if ( ! tooltipItem ) { return null ; } const { isTooltipActive , activeCoordinate , activePayload , activeLabel , offset } = this . state ; return cloneElement ( tooltipItem , { viewBox : { ... offset , x : offset . left , y : offset . top } , active : isTooltipActive , label : activeLabel , payload : isTooltipActive ? activePayload : [ ] , coordinate : activeCoordinate , } ) ; } ; renderBrush = ( element ) => { const { margin , data } = this . props ; const { offset , dataStartIndex , dataEndIndex , updateId } = this . state ; return cloneElement ( element , { key : element . key || '_recharts-brush' , onChange : combineEventHandlers ( this . handleBrushChange , null , element . props . onChange ) , data , x : isNumber ( element . props . x ) ? element . props . x : offset . left , y : isNumber ( element . props . y ) ? element . props . y : offset . top + offset . height + offset . brushBottom - ( margin . bottom || 0 ) , width : isNumber ( element . props . width ) ? element . props . width : offset . width , startIndex : dataStartIndex , endIndex : dataEndIndex , updateId : `brush-${ updateId }` , } ) ; } ; renderReferenceElement = ( element , displayName , index ) : React . ReactElement => { if ( ! element ) { return null ; } const { clipPathId } = this ; const { xAxisMap , yAxisMap , offset } = this . state ; const { xAxisId , yAxisId } = element . props ; return cloneElement ( element , { key : element . key || `${ displayName }-${ index }` , xAxis : xAxisMap [ xAxisId ] , yAxis : yAxisMap [ yAxisId ] , viewBox : { x : offset . left , y : offset . top , width : offset . width , height : offset . height , } , clipPathId , } ) ; } ; static renderActiveDot = ( option , props ) : React . ReactElement => { let dot ; if ( isValidElement ( option ) ) { dot = cloneElement ( option , props ) ; } else if ( _ . isFunction ( option ) ) { dot = option ( props ) ; } else { dot = < Dot { ... props } / > ; } return ( < Layer className = "recharts-active-dot" key = { props . key } > { dot } < /Layer>); } ; renderActivePoints = ( { item , activePoint , basePoint , childIndex , isRange } ) => { const result = [ ] ; const { key } = item . props ; const { activeDot , dataKey } = item . item . props ; const dotProps = { index : childIndex , dataKey , cx : activePoint . x , cy : activePoint . y , r : 4 , fill : getMainColorOfGraphicItem ( item . item ) , strokeWidth : 2 , stroke : '#fff' , payload : activePoint . payload , value : activePoint . value , key : `${ key }-activePoint-${ childIndex }` , ... filterProps ( activeDot ) , ... adaptEventHandlers ( activeDot ) , } ; result . push ( CategoricalChartWrapper . renderActiveDot ( activeDot , dotProps ) ) ; if ( basePoint ) { result . push ( CategoricalChartWrapper . renderActiveDot ( activeDot , { ... dotProps , cx : basePoint . x , cy : basePoint . y , key : `${ key }-basePoint-${ childIndex }` , } ) ) ; } else if ( isRange ) { result . push ( null ) ; } return result ; } ; renderGraphicChild = ( element , displayName , index ) : any [ ] => { const item = this . filterFormatItem ( element , displayName , index ) ; if ( ! item ) { return null ; } const tooltipEventType = this . getTooltipEventType ( ) ; const { isTooltipActive , tooltipAxis , activeTooltipIndex , activeLabel } = this . state ; const { children } = this . props ; const tooltipItem = findChildByType ( children , Tooltip . displayName ) ; const { points , isRange , baseLine } = item . props ; const { activeDot , hide } = item . item . props ; const hasActive = ! hide && isTooltipActive && tooltipItem && activeDot && activeTooltipIndex >= 0 ; let itemEvents = { } ; if ( tooltipEventType !== 'axis' && tooltipItem && tooltipItem . props . trigger === 'click' ) { itemEvents = { onClick : combineEventHandlers ( this . handleItemMouseEnter , null , element . props . onCLick ) , } ; } else if ( tooltipEventType !== 'axis' ) { itemEvents = { onMouseLeave : combineEventHandlers ( this . handleItemMouseLeave , null , element . props . onMouseLeave ) , onMouseEnter : combineEventHandlers ( this . handleItemMouseEnter , null , element . props . onMouseEnter ) , } ; } const graphicalItem = cloneElement ( element , { ... item . props , ... itemEvents } ) ; function findWithPayload ( entry ) { return typeof tooltipAxis . dataKey === 'function' ? tooltipAxis . dataKey ( entry . payload ) : null ; } if ( hasActive ) { let activePoint , basePoint ; if ( tooltipAxis . dataKey && ! tooltipAxis . allowDuplicatedCategory ) { const specifiedKey = typeof tooltipAxis . dataKey === 'function' ? findWithPayload : 'payload.' . concat ( tooltipAxis . dataKey . toString ( ) ) ; activePoint = findEntryInArray ( points , specifiedKey , activeLabel ) ; basePoint = isRange && baseLine && findEntryInArray ( baseLine , specifiedKey , activeLabel ) ; } else { activePoint = points [ activeTooltipIndex ] ; basePoint = isRange && baseLine && baseLine [ activeTooltipIndex ] ; } if ( ! _ . isNil ( activePoint ) ) { return [ graphicalItem , ... this . renderActivePoints ( { item , activePoint , basePoint , childIndex : activeTooltipIndex , isRange , } ) , ] ; } } if ( isRange ) { return [ graphicalItem , null , null ] ; } return [ graphicalItem , null ] ; } ; renderCustomized = ( element , displayName , index ) : React . ReactElement => cloneElement ( element , { key : `recharts-customized-${ index }` , ... this . props , ... this . state , } ) ; renderClipPath ( ) { const { clipPathId } = this ; const { offset : { left , top , height , width } , } = this . state ; return ( < defs > < clipPath id = { clipPathId } > < rect x = { left } y = { top } height = { height } width = { width } / > < /clipPath> < /defs>); } public getXScales ( ) { const { xAxisMap } = this . state ; return xAxisMap ? Object . entries ( xAxisMap ) . reduce ( ( res , [ axisId , axisProps ] ) => { return { ... res , [ axisId ] : ( axisProps as BaseAxisProps ) . scale } ; } , { } ) : null ; } public getYScales ( ) { const { yAxisMap } = this . state ; return yAxisMap ? Object . entries ( yAxisMap ) . reduce ( ( res , [ axisId , axisProps ] ) => { return { ... res , [ axisId ] : ( axisProps as BaseAxisProps ) . scale } ; } , { } ) : null ; } public getXScaleByAxisId ( axisId ) { return this . state . xAxisMap ?. [ axisId ] ?. scale ; } public getYScaleByAxisId ( axisId ) { return this . state . yAxisMap ?. [ axisId ] ?. scale ; } public getItemByXY ( chartXY ) { const { formattedGraphicalItems } = this . state ; if ( formattedGraphicalItems && formattedGraphicalItems . length ) { for ( let i = 0 , len = formattedGraphicalItems . length ; i < len ; i ++ ) { const graphicalItem = formattedGraphicalItems [ i ] ; const { props , item } = graphicalItem ; const itemDisplayName = getDisplayName ( item . type ) ; if ( itemDisplayName === 'Bar' ) { const activeBarItem = ( props . data || [ ] ) . find ( ( entry ) => { return isInRectangle ( chartXY , entry ) ; } ) ; if ( activeBarItem ) { return { graphicalItem , payload : activeBarItem } ; } } else if ( itemDisplayName === 'RadialBar' ) { const activeBarItem = ( props . data || [ ] ) . find ( ( entry ) => { return inRangeOfSector ( chartXY , entry ) ; } ) ; if ( activeBarItem ) { return { graphicalItem , payload : activeBarItem } ; } } } } return null ; } render ( ) { if ( ! validateWidthHeight ( this ) ) { return null ; } const { children , className , width , height , style , compact , ... others } = this . props ; const attrs = filterProps ( others ) ; const map = { CartesianGrid : { handler : this . renderGrid , once : true } , ReferenceArea : { handler : this . renderReferenceElement } , ReferenceLine : { handler : this . renderReferenceElement } , ReferenceDot : { handler : this . renderReferenceElement } , XAxis : { handler : this . renderXAxis } , YAxis : { handler : this . renderYAxis } , Brush : { handler : this . renderBrush , once : true } , Bar : { handler : this . renderGraphicChild } , Line : { handler : this . renderGraphicChild } , Area : { handler : this . renderGraphicChild } , Radar : { handler : this . renderGraphicChild } , RadialBar : { handler : this . renderGraphicChild } , Scatter : { handler : this . renderGraphicChild } , Pie : { handler : this . renderGraphicChild } , Funnel : { handler : this . renderGraphicChild } , Tooltip : { handler : this . renderCursor , once : true } , PolarGrid : { handler : this . renderPolarGrid , once : true } , PolarAngleAxis : { handler : this . renderPolarAxis } , PolarRadiusAxis : { handler : this . renderPolarAxis } , Customized : { handler : this . renderCustomized } , } as any ; if ( compact ) { return ( < Surface { ... attrs } width = { width } height = { height } > { this . renderClipPath ( ) } { renderByOrder ( children , map ) } < /Surface>); } const events = this . parseEventsOfWrapper ( ) ; return ( < div className = { classNames ( 'recharts-wrapper' , className ) } style = { { position : 'relative' , cursor : 'default' , width , height , ... style } } { ... events } ref = { node => { this . container = node ; } } > < Surface { ... attrs } width = { width } height = { height } > { this . renderClipPath ( ) } { renderByOrder ( children , map ) } < /Surface> { this . renderLegend ( ) } { this . renderTooltip ( ) } < /div>); } } ; } ;