import { CSSProperties , MouseEvent } from 'react' ; import { Global } from './Global' ; interface StringCache { widthCache : Record < string , any > ; cacheCount : number ; } const stringCache = { widthCache : { } , cacheCount : 0 , } ; const MAX_CACHE_NUM = 2000 ; const SPAN_STYLE = { position : 'absolute' , top : '-20000px' , left : 0 , padding : 0 , margin : 0 , border : 'none' , whiteSpace : 'pre' , } ; const STYLE_LIST = [ 'minWidth' , 'maxWidth' , 'width' , 'minHeight' , 'maxHeight' , 'height' , 'top' , 'left' , 'fontSize' , 'lineHeight' , 'padding' , 'margin' , 'paddingLeft' , 'paddingRight' , 'paddingTop' , 'paddingBottom' , 'marginLeft' , 'marginRight' , 'marginTop' , 'marginBottom' , ] ; const MEASUREMENT_SPAN_ID = 'recharts_measurement_span' ; function autoCompleteStyle ( name , value ) { if ( STYLE_LIST . indexOf ( name ) >= 0 && value === + value ) { return `${ value }px` ; } return value ; } function camelToMiddleLine ( text ) { const strs = text . split ( '' ) ; const formatStrs = strs . reduce ( ( result , entry ) => { if ( entry === entry . toUpperCase ( ) ) { return [ ... result , '-' , entry . toLowerCase ( ) ] ; } return [ ... result , entry ] ; } , [ ] ) ; return formatStrs . join ( '' ) ; } export const getStyleString = ( style ) => Object . keys ( style ) . reduce ( ( result , s ) => `${ result }${ camelToMiddleLine ( s ) }:${ autoCompleteStyle ( s , ( style as Record < string , any > ) [ s ] ) };` , '' ) ; export const getStringSize = ( text , style = { } ) => { if ( text === undefined || text === null || Global . isSsr ) { return { width : 0 , height : 0 } ; } const str = `${ text }` ; const styleString = getStyleString ( style ) ; const cacheKey = `${ str }-${ styleString }` ; if ( stringCache . widthCache [ cacheKey ] ) { return stringCache . widthCache [ cacheKey ] ; } try { let measurementSpan = document . getElementById ( MEASUREMENT_SPAN_ID ) ; if ( ! measurementSpan ) { measurementSpan = document . createElement ( 'span' ) ; measurementSpan . setAttribute ( 'id' , MEASUREMENT_SPAN_ID ) ; measurementSpan . setAttribute ( 'aria-hidden' , 'true' ) ; document . body . appendChild ( measurementSpan ) ; } const measurementSpanStyle = { ... SPAN_STYLE , ... style } ; Object . keys ( measurementSpanStyle ) . map ( styleKey => { ( measurementSpan . style as Record < string , any > ) [ styleKey ] = measurementSpanStyle [ styleKey ] ; return styleKey ; } ) ; measurementSpan . textContent = str ; const rect = measurementSpan . getBoundingClientRect ( ) ; const result = { width : rect . width , height : rect . height } ; stringCache . widthCache [ cacheKey ] = result ; if ( ++ stringCache . cacheCount > MAX_CACHE_NUM ) { stringCache . cacheCount = 0 ; stringCache . widthCache = { } ; } return result ; } catch ( e ) { return { width : 0 , height : 0 } ; } } ; interface ContainerOffset { top : number ; left : number ; } export const getOffset = ( el ) : ContainerOffset => { const html = el . ownerDocument . documentElement ; let box = { top : 0 , left : 0 } ; if ( typeof el . getBoundingClientRect !== 'undefined' ) { box = el . getBoundingClientRect ( ) ; } return { top : box . top + window . pageYOffset - html . clientTop , left : box . left + window . pageXOffset - html . clientLeft , } ; } ; param { Object } event param { Object } offset return { Object } export const calculateChartCoordinate = ( event , offset ) => ( { chartX : Math . round ( event . pageX - offset . left ) , chartY : Math . round ( event . pageY - offset . top ) , } ) ;