UNPKG

@gooddata/react-components

Version:

GoodData.UI - A powerful JavaScript library for building analytical applications

166 lines (148 loc) • 6.96 kB
// tslint:disable-line /* * * (c) 2010-2019 Torstein Honsi * * Source: https://github.com/highcharts/highcharts/blob/v7.1.1/js/parts-more/BubbleSeries.js * License: www.highcharts.com/license * * Modified by Lan Huynh to to support * - Set default size for bubbles in bubble chart where size value is not provided * - Fix bubbles is not rendered with min/max config */ import isNil = require("lodash/isNil"); import Highcharts from "../highchartsEntryPoint"; import { IHighchartsAxisExtend } from "../../../../../interfaces/HighchartsExtend"; export interface IBubbleAxis extends IHighchartsAxisExtend { allowZoomOutside?: boolean; dataMin?: number; dataMax?: number; options?: any; userMin?: number; userMax?: number; } export interface IBubbleSeries extends Highcharts.Series { bubblePadding?: boolean; minPxSize?: number; maxPxSize?: number; zData?: Array<number | null>; radii?: Array<number | null>; getRadii(zMin: number, zMax: number, series: Highcharts.Series): number | null; } export function renderBubbles(HighchartsInstance: any) { const wrap = HighchartsInstance.wrap; const pInt = HighchartsInstance.pInt; const arrayMax = HighchartsInstance.arrayMax; const arrayMin = HighchartsInstance.arrayMin; const pick = HighchartsInstance.pick; const isNumber = HighchartsInstance.isNumber; if (HighchartsInstance.seriesTypes.bubble) { // Set default size for bubbles in bubble chart where size value is not provided wrap(HighchartsInstance.seriesTypes.bubble.prototype, "getRadius", function( proceed: any, zMin: number, zMax: number, minSize: number, maxSize: number, value: number, ) { let radius = proceed.apply(this, [zMin, zMax, minSize, maxSize, value]); if (isNaN(value) && isNil(radius)) { // Relative size, a number between 0 and 1 (default is 0.5) // Use Math.sqrt for buble is sized by area radius = Math.ceil(minSize + Math.sqrt(0.5) * (maxSize - minSize)) / 2; } return radius; }); // #SD-479 fix bubbles is not rendered with min/max config wrap(HighchartsInstance.Axis.prototype, "beforePadding", function(_proceed: any) { const axis: IBubbleAxis = this; const axisLength: number = this.len; const chart: Highcharts.Chart = this.chart; const isXAxis: boolean = this.isXAxis; const min: number = this.min; const range = this.max - min; const activeSeries: IBubbleSeries[] = []; let pxMin: number = 0; let pxMax: number = axisLength; let transA = axisLength / range; let zMin = Number.MAX_VALUE; let zMax = -Number.MAX_VALUE; function translateSizeToPixel(size: string | number): number { const smallestSize = Math.min(chart.plotWidth, chart.plotHeight); const isPercent = typeof size === "string" ? /%$/.test(size) : false; const pxSize = pInt(size); return isPercent ? (smallestSize * pxSize) / 100 : pxSize; } // Handle padding on the second pass, or on redraw this.series.forEach((series: IBubbleSeries) => { const seriesOptions = series.options; if (series.bubblePadding && (series.visible || !chart.options.chart.ignoreHiddenSeries)) { // Correction for #1673 axis.allowZoomOutside = true; // Cache it activeSeries.push(series); if (isXAxis) { // because X axis is evaluated first // For each series, translate the size extremes to pixel values const minSize = translateSizeToPixel(seriesOptions.minSize); const maxSize = translateSizeToPixel(seriesOptions.maxSize); series.minPxSize = minSize; // Prioritize min size if conflict to make sure bubbles are // always visible. #5873 series.maxPxSize = Math.max(maxSize, minSize); // Find the min and max Z const zData = series.zData.filter(isNumber); if (zData.length) { // #1735 zMin = pick( seriesOptions.zMin, Math.min( zMin, Math.max( arrayMin(zData), seriesOptions.displayNegative === false ? seriesOptions.zThreshold : -Number.MAX_VALUE, ), ), ); zMax = pick(seriesOptions.zMax, Math.max(zMax, arrayMax(zData))); } } } }); activeSeries.forEach((series: IBubbleSeries) => { const dataKey: string = isXAxis ? "xData" : "yData"; const data = series[dataKey]; let i = data.length; if (isXAxis) { series.getRadii(zMin, zMax, series); } if (range > 0) { while (i--) { if (isNumber(data[i]) && axis.dataMin <= data[i] && data[i] <= axis.dataMax) { const radius = series.radii[i]; pxMin = Math.min((data[i] - min) * transA - radius, pxMin); pxMax = Math.max((data[i] - min) * transA + radius, pxMax); } } } }); // Apply the padding to the min and max properties if (activeSeries.length && range > 0 && !this.isLog) { pxMax -= axisLength; // Note for Highchart upgrading later: // - Modified the calculation of transA only // - And transform from javascript to typescripts const pxRange = Math.abs(Math.max(0, pxMin) - Math.min(pxMax, axisLength)); transA *= (axisLength + pxRange) / axisLength; if (pick(axis.options.min, axis.userMin) === undefined) { axis.min += pxMin / transA; } if (pick(axis.options.max, axis.userMax) === undefined) { axis.max += pxMax / transA; } } }); } }