import React, { useMemo } from 'react'; import { makePlotlyPlotComponent, PlotProps } from './PlotlyPlot'; import { Layout, Annotations } from 'plotly.js'; import { HeatmapData } from '../types/plots'; export interface HeatmapProps extends PlotProps { /** Label for x axis. Both x and y axes have "independent" variables */ // TO DO: rename to independent and dependent - see https://epvb.slack.com/archives/C012X6FPVB4/p1624543299066000 xAxisLabel?: string; /** Label for y axis. */ yAxisLabel?: string; /** Show values as text in heatmap */ showValues?: boolean; //DKDK add zsmooth here so that it can be treated as a Props out of data zSmooth?: 'fast' | 'best' | false; } const EmptyHeatmapData: HeatmapData = { xLabels: [], yLabels: [], values: [[]], }; const Heatmap = makePlotlyPlotComponent('Heatmap', (props: HeatmapProps) => { const { data = EmptyHeatmapData, xAxisLabel, yAxisLabel, showValues, zSmooth, legendTitle, // needs to be handled here via colorbar, not main legend in PlotlyPlot ...restProps } = props; const isEmptyData = useMemo(() => data.values.flat().length == 0, [data]); const layout: Partial = { //DKDK set annotations for displaying values at each cell annotations: [], xaxis: { title: xAxisLabel, mirror: true, showgrid: false, tickfont: isEmptyData ? { color: 'transparent' } : {}, zeroline: false, // for empty plot, mainly nticks: isEmptyData ? 5 : data.xLabels.length + 1, }, yaxis: { title: yAxisLabel, mirror: true, showgrid: false, tickfont: isEmptyData ? { color: 'transparent' } : {}, zeroline: false, // for empty plot, mainly nticks: isEmptyData ? 5 : data.yLabels.length + 1, }, }; //DKDK add value texts at each cell when props.showValue = true if (showValues) { for (let i = 0; i < data.yLabels.length; i++) { for (let j = 0; j < data.xLabels.length; j++) { let result: Partial = { xref: 'x', yref: 'y', x: data.xLabels[j], y: data.yLabels[i], //DKDK zValues(i.e., z) is number[][] so converting it into string to avoid type error text: data.values[i][j]?.toString(), font: { color: 'white', // TO DO: generate color more intelligently // taking palette/gradient into consideration too // (white works just-about OK on top of the default blue-grey-red gradient) }, showarrow: false, }; //DKDK set condition to avoid type error layout.annotations ? layout.annotations.push(result) : []; } } } const finalData = [ { x: data.xLabels, y: data.yLabels, z: data.values, type: 'heatmap' as const, zsmooth: zSmooth, colorbar: { title: { text: legendTitle, }, }, }, ]; return { data: finalData, layout, ...restProps, }; }); export default Heatmap;