import React , { Children , ReactNode } from 'react' ; import _ from 'lodash' ; import { isFragment } from 'react-is' ; import { isNumber } from './DataUtils' ; import { shallowEqual } from './ShallowEqual' ; const REACT_BROWSER_EVENT_MAP = { click : 'onClick' , mousedown : 'onMouseDown' , mouseup : 'onMouseUp' , mouseover : 'onMouseOver' , mousemove : 'onMouseMove' , mouseout : 'onMouseOut' , mouseenter : 'onMouseEnter' , mouseleave : 'onMouseLeave' , touchcancel : 'onTouchCancel' , touchend : 'onTouchEnd' , touchmove : 'onTouchMove' , touchstart : 'onTouchStart' , } ; export const SCALE_TYPES = [ 'auto' , 'linear' , 'pow' , 'sqrt' , 'log' , 'identity' , 'time' , 'band' , 'point' , 'ordinal' , 'quantile' , 'quantize' , 'utc' , 'sequential' , 'threshold' , ] ; export const LEGEND_TYPES = [ 'plainline' , 'line' , 'square' , 'rect' , 'circle' , 'cross' , 'diamond' , 'star' , 'triangle' , 'wye' , 'none' , ] ; export const TOOLTIP_TYPES = [ 'none' ] ; param { Object } Comp return { String } export const getDisplayName = ( Comp ) => { if ( typeof Comp === 'string' ) { return Comp ; } if ( ! Comp ) { return '' ; } return Comp . displayName || Comp . name || 'Component' ; } ; let lastChildren = null ; let lastResult = null ; export const toArray = < T extends ReactNode > ( children ) : T [ ] => { if ( children === lastChildren && _ . isArray ( lastResult ) ) { return lastResult as T [ ] ; } let result = [ ] ; Children . forEach ( children , child => { if ( _ . isNil ( child ) ) return ; if ( isFragment ( child ) ) { result = result . concat ( toArray ( child . props . children ) ) ; } else { result . push ( child ) ; } } ) ; lastResult = result ; lastChildren = children ; return result ; } ; export const findAllByType = ( children , type ) : React . DetailedReactHTMLElement < any , HTMLElement > [ ] => { const result = [ ] ; let types = [ ] ; if ( _ . isArray ( type ) ) { types = type . map ( t => getDisplayName ( t ) ) ; } else { types = [ getDisplayName ( type ) ] ; } toArray ( children ) . forEach ( ( child ) => { const childType = _ . get ( child , 'type.displayName' ) || _ . get ( child , 'type.name' ) ; if ( types . indexOf ( childType ) !== - 1 ) { result . push ( child ) ; } } ) ; return result ; } ; export const findChildByType = ( children , type ) : React . DetailedReactHTMLElement < any , HTMLElement > => { const result = findAllByType ( children , type ) ; return result && result [ 0 ] ; } ; export const withoutType = ( children , type ) => { const newChildren = [ ] ; let types ; if ( _ . isArray ( type ) ) { types = type . map ( t => getDisplayName ( t ) ) ; } else { types = [ getDisplayName ( type ) ] ; } toArray ( children ) . forEach ( child => { const displayName = _ . get ( child , 'type.displayName' ) ; if ( displayName && types . indexOf ( displayName ) !== - 1 ) { return ; } newChildren . push ( child ) ; } ) ; return newChildren ; } ; param { Object } el return { Boolean } export const validateWidthHeight = ( el ) : boolean => { if ( ! el || ! el . props ) { return false ; } const { width , height } = el . props ; if ( ! isNumber ( width ) || width <= 0 || ! isNumber ( height ) || height <= 0 ) { return false ; } return true ; } ; const SVG_TAGS = [ 'a' , 'altGlyph' , 'altGlyphDef' , 'altGlyphItem' , 'animate' , 'animateColor' , 'animateMotion' , 'animateTransform' , 'circle' , 'clipPath' , 'color-profile' , 'cursor' , 'defs' , 'desc' , 'ellipse' , 'feBlend' , 'feColormatrix' , 'feComponentTransfer' , 'feComposite' , 'feConvolveMatrix' , 'feDiffuseLighting' , 'feDisplacementMap' , 'feDistantLight' , 'feFlood' , 'feFuncA' , 'feFuncB' , 'feFuncG' , 'feFuncR' , 'feGaussianBlur' , 'feImage' , 'feMerge' , 'feMergeNode' , 'feMorphology' , 'feOffset' , 'fePointLight' , 'feSpecularLighting' , 'feSpotLight' , 'feTile' , 'feTurbulence' , 'filter' , 'font' , 'font-face' , 'font-face-format' , 'font-face-name' , 'font-face-url' , 'foreignObject' , 'g' , 'glyph' , 'glyphRef' , 'hkern' , 'image' , 'line' , 'lineGradient' , 'marker' , 'mask' , 'metadata' , 'missing-glyph' , 'mpath' , 'path' , 'pattern' , 'polygon' , 'polyline' , 'radialGradient' , 'rect' , 'script' , 'set' , 'stop' , 'style' , 'svg' , 'switch' , 'symbol' , 'text' , 'textPath' , 'title' , 'tref' , 'tspan' , 'use' , 'view' , 'vkern' , ] ; const isSvgElement = ( child ) => child && child . type && _ . isString ( child . type ) && SVG_TAGS . indexOf ( child . type ) >= 0 ; param { Array } children return { Array } export const filterSvgElements = ( children ) : React . ReactElement [ ] => { const svgElements = [ ] as React . ReactElement [ ] ; toArray ( children ) . forEach ( ( entry ) => { if ( isSvgElement ( entry ) ) { svgElements . push ( entry ) ; } } ) ; return svgElements ; } ; param { Object } nextChildren param { Object } prevChildren return { Boolean } export const isChildrenEqual = ( nextChildren , prevChildren ) : boolean => { if ( nextChildren === prevChildren ) { return true ; } const count = Children . count ( nextChildren ) ; if ( count !== Children . count ( prevChildren ) ) { return false ; } if ( count === 0 ) { return true ; } if ( count === 1 ) { return isSingleChildEqual ( _ . isArray ( nextChildren ) ? nextChildren [ 0 ] : nextChildren , _ . isArray ( prevChildren ) ? prevChildren [ 0 ] : prevChildren ) ; } for ( let i = 0 ; i < count ; i ++ ) { const nextChild = nextChildren [ i ] ; const prevChild = prevChildren [ i ] ; if ( _ . isArray ( nextChild ) || _ . isArray ( prevChild ) ) { if ( ! isChildrenEqual ( nextChild , prevChild ) ) { return false ; } } else if ( ! isSingleChildEqual ( nextChild , prevChild ) ) { return false ; } } return true ; } ; export const isSingleChildEqual = ( nextChild , prevChild ) : boolean => { if ( _ . isNil ( nextChild ) && _ . isNil ( prevChild ) ) { return true ; } if ( ! _ . isNil ( nextChild ) && ! _ . isNil ( prevChild ) ) { const { children : nextChildren , ... nextProps } = nextChild . props || { } ; const { children : prevChildren , ... prevProps } = prevChild . props || { } ; if ( nextChildren && prevChildren ) { return shallowEqual ( nextProps , prevProps ) && isChildrenEqual ( nextChildren , prevChildren ) ; } if ( ! nextChildren && ! prevChildren ) { return shallowEqual ( nextProps , prevProps ) ; } return false ; } return false ; } ; export const renderByOrder = ( children , renderMap ) => { const elements = [ ] ; const record = { } ; toArray ( children ) . forEach ( ( child , index ) => { if ( isSvgElement ( child ) ) { elements . push ( child ) ; } else if ( child ) { const displayName = getDisplayName ( child . type ) ; const { handler , once } = renderMap [ displayName ] || { } ; if ( handler && ( ! once || ! record [ displayName ] ) ) { const results = handler ( child , displayName , index ) ; elements . push ( results ) ; record [ displayName ] = true ; } } } ) ; return elements ; } ; export const getReactEventByType = ( e ) => { const type = e && e . type ; if ( type && REACT_BROWSER_EVENT_MAP [ type ] ) { return REACT_BROWSER_EVENT_MAP [ type ] ; } return null ; } ; export const parseChildIndex = ( child , children ) => { return toArray ( children ) . indexOf ( child ) ; } ;