private updateInternal()

in src/visual.ts [385:690]


    private updateInternal(options: VisualUpdateOptions, settings: Settings): void {
        let dataView: DataView = this.dataView = options.dataViews[0];
        let chartData: TableHeatMapChartData = this.converter(dataView, this.colors);
        let suppressAnimations: boolean = false;
        if (chartData.dataPoints) {
            let minDataValue: number = d3.min(chartData.dataPoints, function (d: TableHeatMapDataPoint) {
                return d.value as number;
            });
            let maxDataValue: number = d3.max(chartData.dataPoints, function (d: TableHeatMapDataPoint) {
                return d.value as number;
            });

            let numBuckets: number = settings.general.buckets;
            let colorbrewerScale: string = settings.general.colorbrewer;
            let colorbrewerEnable: boolean = settings.general.enableColorbrewer;
            let colors: Array<string>;
            if (colorbrewerEnable) {
                if (colorbrewerScale) {
                    let currentColorbrewer: IColorArray = colorbrewer[colorbrewerScale];
                    colors = (currentColorbrewer ? currentColorbrewer[numBuckets] : colorbrewer.Reds[numBuckets]);
                }
                else {
                    colors = colorbrewer.Reds[numBuckets];	// default color scheme
                }
            } else {
                let startColor: string = settings.general.gradientStart;
                let endColor: string = settings.general.gradientEnd;
                let colorScale: LinearColorScale = createLinearColorScale([0, numBuckets], [startColor, endColor], true);
                colors = [];

                for (let bucketIndex: number = 0; bucketIndex < numBuckets; bucketIndex++) {
                    colors.push(colorScale(bucketIndex));
                }
            }

            let colorScale: Quantile<string> = d3.scaleQuantile<string>()
                .domain([minDataValue, maxDataValue])
                .range(colors);

            let xAxisHeight: number = this.getXAxisHeight(chartData);
            let yAxisWidth: number = this.getYAxisWidth(chartData);
            let yAxisHeight: number = this.getYAxisHeight(chartData);

            if (!settings.yAxisLabels.show) {
                yAxisWidth = 0;
            }

            if (!settings.xAxisLabels.show) {
                xAxisHeight = 0;
            }

            let maxDataText: string = chartData.dataPoints[0].valueStr || "";
            chartData.dataPoints.forEach((value: TableHeatMapDataPoint) => {
                if ((value.valueStr || "").length > maxDataText.length) {
                    maxDataText = value.valueStr || "";
                }
            });

            let textProperties: TextProperties = {
                fontSize: PixelConverter.toString(settings.labels.fontSize),
                fontFamily: settings.labels.fontFamily,
                text: maxDataText
            };
            let textRect: SVGRect = TextMeasurementService.measureSvgTextRect(textProperties);

            let gridSizeWidth: number = Math.floor((this.viewport.width - yAxisWidth) / (chartData.categoryX.length));
            let gridSizeHeight: number = gridSizeWidth * TableHeatMap.ConstGridHeightWidthRaito;

            if (gridSizeWidth < textRect.width && settings.labels.show) {
                gridSizeWidth = textRect.width;
            }
            if (gridSizeHeight < textRect.height && settings.labels.show) {
                gridSizeHeight = textRect.height;
            }
            if (gridSizeHeight > TableHeatMap.CellMaxHeightLimit) {
                gridSizeHeight = TableHeatMap.CellMaxHeightLimit;
            }
            if (gridSizeWidth > gridSizeHeight * TableHeatMap.CellMaxWidthFactorLimit) {
                gridSizeWidth = gridSizeHeight * TableHeatMap.CellMaxWidthFactorLimit;
            }

            if (gridSizeHeight < TableHeatMap.ConstGridMinHeight) {
                gridSizeHeight = TableHeatMap.ConstGridMinHeight;
            }
            if (gridSizeWidth < TableHeatMap.ConstGridMinWidth) {
                gridSizeWidth = TableHeatMap.ConstGridMinWidth;
            }

            let xOffset: number = this.margin.left + yAxisWidth; // add widht of y labels width
            let yOffset: number = this.margin.top + xAxisHeight; // todo add height of x categoru labels height

            const TableHeatMapCellRaito: number = 2 / 3;
            let legendElementWidth: number = (this.viewport.width * TableHeatMapCellRaito - xOffset) / numBuckets;
            let legendElementHeight: number = gridSizeHeight;

            if (settings.yAxisLabels.show) {
                let categoryYElements:  d3.Selection<d3.BaseType, any, any, any> = this.mainGraphics.selectAll("." + TableHeatMap.ClsCategoryYLabel);
                let categoryYElementsData = categoryYElements
                    .data(chartData.categoryY);
                let categoryYElementsEntered = categoryYElementsData
                    .enter()
                    .append(TableHeatMap.HtmlObjText);

                categoryYElementsEntered.exit().remove();

                let categoryYElementsMerged = categoryYElementsEntered.merge(categoryYElements);

                categoryYElementsMerged
                    .text((d: string) => {
                        return TableHeatMap.textLimit(d, settings.yAxisLabels.maxTextSymbol);
                    })
                    .attr(TableHeatMap.AttrDY, TableHeatMap.Const071em)
                    .attr(TableHeatMap.AttrX, this.margin.left)
                    .attr(TableHeatMap.AttrY, function (d, i) {
                        return i * gridSizeHeight - (gridSizeHeight / 2) + yOffset - yAxisHeight / 3;
                    })
                    .style(TableHeatMap.StTextAnchor, TableHeatMap.ConstBegin)
                    .style("font-size", settings.yAxisLabels.fontSize)
                    .style("font-family", settings.yAxisLabels.fontFamily)
                    .style("fill", settings.yAxisLabels.fill)
                    .attr(TableHeatMap.AttrTransform, translate(TableHeatMap.ConstShiftLabelFromGrid, gridSizeHeight))
                    .classed(TableHeatMap.ClsCategoryYLabel, true)
                    .classed(TableHeatMap.ClsMono, true)
                    .classed(TableHeatMap.ClsAxis, true);

                this.mainGraphics.selectAll("." + TableHeatMap.ClsCategoryYLabel)
                    .call(this.wrap, gridSizeWidth + xOffset);

                this.truncateTextIfNeeded(this.mainGraphics.selectAll("." + TableHeatMap.ClsCategoryYLabel), gridSizeWidth + xOffset);
            }

            if (settings.xAxisLabels.show) {
                let categoryXElements:  d3.Selection<d3.BaseType, any, any, any> =  this.mainGraphics.selectAll("." + TableHeatMap.ClsCategoryXLabel);
                let categoryXElementsData = categoryXElements
                    .data(chartData.categoryX);
                categoryXElementsData.exit().remove();
                let categoryXElementsEntered = categoryXElementsData
                    .enter().append(TableHeatMap.HtmlObjText);
                let categoryXElementsMerged = categoryXElementsEntered.merge(categoryXElements);

                categoryXElementsMerged
                    .text(function (d: string) {
                        return chartData.categoryValueFormatter.format(d);
                    })
                    .attr(TableHeatMap.AttrX, function (d: string, i: number) {
                        return i * gridSizeWidth + xOffset;
                    })
                    .attr(TableHeatMap.AttrY, xAxisHeight / 2)
                    .attr(TableHeatMap.AttrDY, TableHeatMap.Const0em)
                    .style(TableHeatMap.StTextAnchor, TableHeatMap.ConstMiddle)
                    .style("font-size", settings.xAxisLabels.fontSize)
                    .style("font-family", settings.xAxisLabels.fontFamily)
                    .style("fill", settings.xAxisLabels.fill)
                    .classed(TableHeatMap.ClsCategoryXLabel + " " + TableHeatMap.ClsMono + " " + TableHeatMap.ClsAxis, true)
                    .attr(TableHeatMap.AttrTransform, translate(gridSizeHeight, TableHeatMap.ConstShiftLabelFromGrid));

                this.truncateTextIfNeeded(this.mainGraphics.selectAll("." + TableHeatMap.ClsCategoryXLabel), gridSizeWidth);
            }

            let heatMap: Selection<TableHeatMapDataPoint> = this.mainGraphics.selectAll("." + TableHeatMap.ClsCategoryX);
            let heatMapData = heatMap
                .data(chartData.dataPoints);
            let heatMapEntered = heatMapData
                .enter()
                .append(TableHeatMap.HtmlObjRect);
            let heatMapMerged = heatMapEntered.merge(heatMap);

            heatMapMerged
                .attr(TableHeatMap.AttrX, function (d: TableHeatMapDataPoint) {
                    return chartData.categoryX.indexOf(d.categoryX) * gridSizeWidth + xOffset;
                })
                .attr(TableHeatMap.AttrY, function (d: TableHeatMapDataPoint) {
                    return chartData.categoryY.indexOf(d.categoryY) * gridSizeHeight + yOffset;
                })
                .classed(TableHeatMap.ClsCategoryX + " " + TableHeatMap.ClsBordered, true)
                .attr(TableHeatMap.AttrWidth, gridSizeWidth)
                .attr(TableHeatMap.AttrHeight, gridSizeHeight)
                .style(TableHeatMap.StFill, colors[0])
                .style("stroke", settings.general.stroke);


            if (chartData.categoryX.length * gridSizeWidth + xOffset > options.viewport.width) {
                this.svg.attr("width", chartData.categoryX.length * gridSizeWidth);
            }

            // add data labels
            let textHeight: number = textRect.height;
            let heatMapDataLables: Selection<TableHeatMapDataPoint> = this.mainGraphics.selectAll("." + TableHeatMap.CLsHeatMapDataLabels);

            if (settings.labels.show && textHeight <= gridSizeHeight) {
                let heatMapDataLablesData: Selection<TableHeatMapDataPoint> = heatMapDataLables.data(chartData.dataPoints);
                heatMapDataLables.exit().remove();

                let heatMapDataLablesEntered = heatMapDataLablesData
                    .enter().append("text");

                heatMapDataLablesEntered
                    .classed(TableHeatMap.CLsHeatMapDataLabels, true)
                    .attr(TableHeatMap.AttrX, function (d: TableHeatMapDataPoint) {
                        return chartData.categoryX.indexOf(d.categoryX) * gridSizeWidth + xOffset + gridSizeWidth / 2;
                    })
                    .attr(TableHeatMap.AttrY, function (d: TableHeatMapDataPoint) {
                        return chartData.categoryY.indexOf(d.categoryY) * gridSizeHeight + yOffset + gridSizeHeight / 2 + textHeight / 2.6;
                    })
                    .style("text-anchor", TableHeatMap.ConstMiddle)
                    .style("font-size", this.settings.labels.fontSize)
                    .style("font-family", this.settings.labels.fontFamily)
                    .style("fill", this.settings.labels.fill)
                    .text((dataPoint: TableHeatMapDataPoint) => {
                        let textValue: string = ValueFormatter.format(dataPoint.value);
                        textProperties.text = textValue;
                        textValue = TextMeasurementService.getTailoredTextOrDefault(textProperties, gridSizeWidth);
                        return dataPoint.value === 0 ? 0 : textValue;
                    });
            }

            let elementAnimation: Selection<D3Element> = <Selection<D3Element>>this.getAnimationMode(heatMapMerged, suppressAnimations);
            if (!this.settings.general.fillNullValuesCells) {
                heatMapMerged.style(TableHeatMap.StOpacity, function (d: any) {
                    return d.value === null ? 0 : 1;
                });
            }
            elementAnimation.style(TableHeatMap.StFill, function (d: any) {
                return <string>colorScale(d.value);
            });

            this.tooltipServiceWrapper.addTooltip(heatMapMerged, (tooltipEvent: TooltipEventArgs<TooltipEnabledDataPoint>) => {
                return tooltipEvent.data.tooltipInfo;
            });

            // legend
            let legendDataValues = [minDataValue].concat(colorScale.quantiles());
            let legendData = legendDataValues.concat(maxDataValue).map((value, index) => {
                return {
                    value: value,
                    tooltipInfo: [{
                        displayName: `Min value`,
                        value: value && typeof value.toFixed === "function" ? value.toFixed(0) : chartData.categoryValueFormatter.format(value)
                    },
                    {
                        displayName: `Max value`,
                        value: legendDataValues[index + 1] && typeof legendDataValues[index + 1].toFixed === "function" ? legendDataValues[index + 1].toFixed(0) : chartData.categoryValueFormatter.format(maxDataValue)
                    }]
                };
            });

            let legendSelection: Selection<any> = this.mainGraphics.selectAll("." + TableHeatMap.ClsLegend);
            let legendSelectionData = legendSelection.data(legendData);

            let legendSelectionEntered = legendSelectionData
                .enter()
                .append(TableHeatMap.HtmlObjG);

            legendSelectionData.exit().remove();

            let legendSelectionMerged = legendSelectionData.merge(legendSelection);
            legendSelectionMerged.classed(TableHeatMap.ClsLegend, true);

            let legendOffsetCellsY: number = this.margin.top
                    + gridSizeHeight * (chartData.categoryY.length + TableHeatMap.ConstLegendOffsetFromChartByY)
                    + xAxisHeight;
                    let legendOffsetTextY: number = this.margin.top
                    - gridSizeHeight / 2
                    + gridSizeHeight * (chartData.categoryY.length + TableHeatMap.ConstLegendOffsetFromChartByY)
                    + legendElementHeight * 2
                    + xAxisHeight;

            legendSelectionEntered
                .append(TableHeatMap.HtmlObjRect)
                .attr(TableHeatMap.AttrX, function (d, i) {
                    return legendElementWidth * i + xOffset;
                })
                .attr(TableHeatMap.AttrY, legendOffsetCellsY)
                .attr(TableHeatMap.AttrWidth, legendElementWidth)
                .attr(TableHeatMap.AttrHeight, legendElementHeight)
                .style(TableHeatMap.StFill, function (d, i) {
                    return colors[i];
                })
                .style("stroke", settings.general.stroke)
                .style("opacity", (d) => d.value !== maxDataValue ? 1 : 0)
                .classed(TableHeatMap.ClsBordered, true);

            legendSelectionEntered
                .append(TableHeatMap.HtmlObjText)
                .classed(TableHeatMap.ClsMono, true)
                .attr(TableHeatMap.AttrX, function (d, i) {
                    return legendElementWidth * i + xOffset;
                })
                .attr(TableHeatMap.AttrY, legendOffsetTextY)
                .text(function (d) {
                    return chartData.valueFormatter.format(d.value);
                })
                .style("fill", settings.general.textColor);

                this.tooltipServiceWrapper.addTooltip(
                    legendSelectionEntered,
                    (tooltipEvent: TooltipEventArgs<TooltipEnabledDataPoint>) => {
                        return tooltipEvent.data.tooltipInfo;
                    }
                );

            if (legendOffsetTextY + gridSizeHeight > options.viewport.height) {
                this.svg.attr("height", legendOffsetTextY + gridSizeHeight);
            }
        }
    }