@gooddata/react-components
Version:
GoodData.UI - A powerful JavaScript library for building analytical applications
121 lines (114 loc) • 4.71 kB
text/typescript
// (C) 2007-2019 GoodData Corporation
import cloneDeep = require("lodash/cloneDeep");
import last = require("lodash/last");
import Highcharts from "./highchartsEntryPoint";
import {
IHighchartsAxisExtend,
IHighchartsSeriesExtend,
IHighchartsSeriesOptionsType,
} from "../../../../interfaces/HighchartsExtend";
function between(x: number, lowerLimit: number, higherLimit: number): number {
return Math.min(Math.max(lowerLimit, x), higherLimit);
}
// Fix Highchart issue https://github.com/highcharts/highcharts/issues/11229
export function resetPointPaddingForTooSmallHeatmapCells(series: IHighchartsSeriesExtend) {
const xAxis: IHighchartsAxisExtend = series.xAxis;
const yAxis: IHighchartsAxisExtend = series.yAxis;
const options: IHighchartsSeriesOptionsType = series.options;
const seriesPointPadding: number = options.pointPadding || 0;
const pointPlacement: number = series.pointPlacementToXValue();
series.points.forEach((point: Highcharts.Point) => {
// Recalculate (x1, x2, y1, y2) <=> (left, right, top, bottom) of heatmap cells in pixels
// Transform the calculation of x1, x2, y1, y2 from javascripts to typescripts
// From source: https://github.com/highcharts/highcharts/blob/v7.1.1/js/parts-map/HeatmapSeries.js#L222
const xPad: number = (options.colsize || 1) / 2;
const yPad: number = (options.rowsize || 1) / 2;
const x1 = between(
Math.round(xAxis.len - xAxis.translate(point.x - xPad, 0, 1, 0, 1, -pointPlacement)),
-xAxis.len,
2 * xAxis.len,
);
const x2 = between(
Math.round(xAxis.len - xAxis.translate(point.x + xPad, 0, 1, 0, 1, -pointPlacement)),
-xAxis.len,
2 * xAxis.len,
);
const y1 = between(
Math.round(yAxis.translate(point.y - yPad, 0, 1, 0, 1)),
-yAxis.len,
2 * yAxis.len,
);
const y2 = between(
Math.round(yAxis.translate(point.y + yPad, 0, 1, 0, 1)),
-yAxis.len,
2 * yAxis.len,
);
const pointPadding: number = point.pointPadding || seriesPointPadding;
// width: Math.abs(x2 - x1) - pointPadding * 2
// height: Math.abs(y2 - y1) - pointPadding * 2
// Note for highchart upgrading later:
// 1. The modification reset only pointPadding property by below `if`.
// 2. Another parts just transform javascript to typescripts.
if (Math.abs(x2 - x1) < pointPadding * 2 || Math.abs(y2 - y1) < pointPadding * 2) {
// reset pointPadding if new width/Height is negative number
point.pointPadding = 0;
}
});
}
const HEATMAP_TEMPLATE = {
chart: {
type: "heatmap",
marginTop: 8,
marginRight: 0,
spacingRight: 0,
},
plotOptions: {
heatmap: {
dataLabels: {
enabled: true,
allowOverlap: false,
crop: true,
overflow: "justify",
},
point: {
events: {
// from Highcharts 5.0.0 cursor can be set by using 'className' for individual data items
mouseOver() {
if (this.drilldown) {
this.graphic.element.style.cursor = "pointer";
}
},
},
},
events: {
afterGeneratePoints() {
resetPointPaddingForTooSmallHeatmapCells(this);
},
},
},
},
yAxis: [
{
labels: {
formatter() {
const { axis, isLast } = this;
const { tickPositions, categories } = axis;
// tickPositions is array of index of categories
const lastIndex = parseInt(last(tickPositions).toString(), 10);
const lastCategory = categories ? categories[lastIndex] : null;
let labelValue = axis.defaultLabelFormatter.call(this);
// When generate linear tick positions base on categories length.
// Last tick position can be out of index of categories.
// In this case, set label value to null to ignore last label.
if (isLast && categories && !lastCategory) {
labelValue = null;
}
return labelValue;
},
},
},
],
};
export function getHeatmapConfiguration() {
return cloneDeep(HEATMAP_TEMPLATE);
}