Skip to content

Commit

Permalink
feat: responsive heatmap (apache#989)
Browse files Browse the repository at this point in the history
* feat: responsive heatmap

* Rotate labels

* Update story for heatmap chart

* Add for withNullData chart

* Resizable
  • Loading branch information
maloun96 authored and zhaoyongjie committed Nov 24, 2021
1 parent 36d8f5b commit cb68284
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { SuperChart } from '@superset-ui/core';
import HeatmapChartPlugin from '@superset-ui/legacy-plugin-chart-heatmap';
import ResizableChartDemo from '../../../shared/components/ResizableChartDemo';
import data from './data';

new HeatmapChartPlugin().configure({ key: 'heatmap' }).register();
Expand All @@ -12,8 +13,8 @@ export default {
export const basic = () => (
<SuperChart
chartType="heatmap"
width={400}
height={400}
width={500}
height={500}
formData={{
allColumnsX: 'source',
allColumnsY: 'target',
Expand Down Expand Up @@ -44,11 +45,50 @@ export const basic = () => (
/>
);

export const resizable = () => (
<ResizableChartDemo>
{({ width, height }) => (
<SuperChart
chartType="heatmap"
width={width}
height={height}
formData={{
allColumnsX: 'source',
allColumnsY: 'target',
bottomMargin: 'auto',
canvasImageRendering: 'pixelated',
leftMargin: 'auto',
linearColorScheme: 'blue_white_yellow',
metric: 'sum__value',
normalized: false,
showLegend: true,
showPerc: true,
showValues: false,
sortXAxis: 'alpha_asc',
sortYAxis: 'alpha_asc',
xscaleInterval: '1',
yAxisBounds: [null, null],
yAxisFormat: '.3s',
yscaleInterval: '1',
}}
queriesData={[
{
data: {
records: data,
extents: [0.1, 24.9],
},
},
]}
/>
)}
</ResizableChartDemo>
);

export const withNullData = () => (
<SuperChart
chartType="heatmap"
width={400}
height={400}
width={500}
height={500}
formData={{
allColumnsX: 'source',
allColumnsY: 'target',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
}

.superset-legacy-chart-heatmap .axis text {
font: 12px sans-serif;
font: 10px sans-serif;
text-rendering: optimizeLegibility;
fill: #555;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ function cmp(a, b) {
return a > b ? 1 : -1;
}

const DEFAULT_PROPERTIES = {
minChartWidth: 150,
minChartHeight: 150,
marginLeft: 35,
marginBottom: 35,
marginTop: 10,
marginRight: 10,
};

// Inspired from http://bl.ocks.org/mbostock/3074470
// https://jsfiddle.net/cyril123/h0reyumq/
function Heatmap(element, props) {
Expand Down Expand Up @@ -97,12 +106,16 @@ function Heatmap(element, props) {
bottom: 35,
left: 35,
};

let showY = true;
let showX = true;
const pixelsPerCharX = 4.5; // approx, depends on font size
const pixelsPerCharY = 6; // approx, depends on font size

const valueFormatter = getNumberFormatter(numberFormat);

// Dynamically adjusts based on max x / y category lengths
function adjustMargins() {
const pixelsPerCharX = 4.5; // approx, depends on font size
const pixelsPerCharY = 6; // approx, depends on font size
let longestX = 1;
let longestY = 1;

Expand All @@ -127,6 +140,25 @@ function Heatmap(element, props) {
: bottomMargin;
}

// Check if x axis "x" position is outside of the container and rotate labels 90deg
function checkLabelPosition(container) {
const xAxisNode = container.select('.x.axis').node();

if (!xAxisNode) {
return;
}

if (xAxisNode.getBoundingClientRect().x + 4 < container.node().getBoundingClientRect().x) {
container
.selectAll('.x.axis')
.selectAll('text')
.attr('transform', 'rotate(-90)')
.attr('x', -6)
.attr('y', 0)
.attr('dy', '0.3em');
}
}

function ordScale(k, rangeBands, sortMethod) {
let domain = {};
const actualKeys = {}; // hack to preserve type of keys when number
Expand Down Expand Up @@ -163,8 +195,34 @@ function Heatmap(element, props) {

adjustMargins();

const hmWidth = width - (margin.left + margin.right);
const hmHeight = height - (margin.bottom + margin.top);
let hmWidth = width - (margin.left + margin.right);
let hmHeight = height - (margin.bottom + margin.top);
const hideYLabel = () => {
margin.left = leftMargin === 'auto' ? DEFAULT_PROPERTIES.marginLeft : leftMargin;
hmWidth = width - (margin.left + margin.right);
showY = false;
};

const hideXLabel = () => {
margin.bottom = bottomMargin === 'auto' ? DEFAULT_PROPERTIES.marginBottom : bottomMargin;
hmHeight = height - (margin.bottom + margin.top);
showX = false;
};

// Hide Y Labels
if (hmWidth < DEFAULT_PROPERTIES.minChartWidth) {
hideYLabel();
}

// Hide X Labels
if (hmHeight < DEFAULT_PROPERTIES.minChartHeight || hmWidth < DEFAULT_PROPERTIES.minChartWidth) {
hideXLabel();
}

if (showY && hmHeight < DEFAULT_PROPERTIES.minChartHeight) {
hideYLabel();
}

const fp = getNumberFormatter(NumberFormats.PERCENT);

const xScale = ordScale('x', null, sortXAxis);
Expand Down Expand Up @@ -204,6 +262,7 @@ function Heatmap(element, props) {
.append('svg')
.attr('width', width)
.attr('height', height)
.attr('class', 'heatmap-container')
.style('position', 'relative');

if (showValues) {
Expand Down Expand Up @@ -287,38 +346,43 @@ function Heatmap(element, props) {

rect.call(tip);

const xAxis = d3.svg
.axis()
.scale(xRbScale)
.outerTickSize(0)
.tickValues(xRbScale.domain().filter((d, i) => !(i % xScaleInterval)))
.orient('bottom');

const yAxis = d3.svg
.axis()
.scale(yRbScale)
.outerTickSize(0)
.tickValues(yRbScale.domain().filter((d, i) => !(i % yScaleInterval)))
.orient('left');

svg
.append('g')
.attr('class', 'x axis')
.attr('transform', `translate(${margin.left},${margin.top + hmHeight})`)
.call(xAxis)
.selectAll('text')
.attr('x', -4)
.attr('y', 10)
.attr('dy', '0.3em')
.style('text-anchor', 'end')
.attr('transform', 'rotate(-45)');

svg
.append('g')
.attr('class', 'y axis')
.attr('transform', `translate(${margin.left},${margin.top})`)
.call(yAxis);
if (showX) {
const xAxis = d3.svg
.axis()
.scale(xRbScale)
.outerTickSize(0)
.tickValues(xRbScale.domain().filter((d, i) => !(i % xScaleInterval)))
.orient('bottom');

svg
.append('g')
.attr('class', 'x axis')
.attr('transform', `translate(${margin.left},${margin.top + hmHeight})`)
.call(xAxis)
.selectAll('text')
.attr('x', -4)
.attr('y', 10)
.attr('dy', '0.3em')
.style('text-anchor', 'end')
.attr('transform', 'rotate(-45)');
}

if (showY) {
const yAxis = d3.svg
.axis()
.scale(yRbScale)
.outerTickSize(0)
.tickValues(yRbScale.domain().filter((d, i) => !(i % yScaleInterval)))
.orient('left');

svg
.append('g')
.attr('class', 'y axis')
.attr('transform', `translate(${margin.left},${margin.top})`)
.call(yAxis);
}

checkLabelPosition(container);
const context = canvas.node().getContext('2d');
context.imageSmoothingEnabled = false;

Expand Down

0 comments on commit cb68284

Please sign in to comment.