private drawTooltips()

in src/visual.ts [1688:1933]


    private drawTooltips(data: ChartData): void {
        let xScale: LinearScale = <LinearScale>data.xScale,
            yScales: LinearScale[] = <LinearScale[]>data.yScales,
            node: ClassAndSelector = Visual.Tooltip,
            nodeParent: ClassAndSelector = Visual.TooltipContainer,
            width: number = this.data.settings.popup.width,
            height: number = this.data.settings.popup.height,
            marginTop: number = Visual.DefaultTooltipSettings.marginTop,
            showTimeDisplayProperty: string = this.data.settings.popup.showTime ? "inherit" : "none",
            showTitleDisplayProperty: string = this.data.settings.popup.showTitle ? "inherit" : "none";

        let rootSelection: Selection<any> = this.rootSelection;

        let line: Line = d3Line<PointXY>()
            .x((d: PointXY) => d.x)
            .y((d: PointXY) => {
                return d.y;
            });

        let tooltipShiftY = (y: number, groupIndex: number): number => {
            return this.isHigherMiddle(y, groupIndex) ? (-1 * marginTop + Visual.topShift) : this.size.height + marginTop;
        };

        let tooltipRoot: Selection<any> = rootSelection.select(nodeParent.selectorName).selectAll(node.selectorName)
            .data(d => {
                return filter(d.data, (value: DataPoint) => this.isPopupShow(value));
            });

        let tooltipRootMerged = tooltipRoot
            .enter()
            .append("g")
            .merge(tooltipRoot);
        tooltipRootMerged.classed(node.className, true);

        tooltipRootMerged
            .attr("transform", (d: DataPoint) => {
                let x: number = xScale(d.x) - width / 2;
                let y: number = tooltipShiftY(d.y, d.groupIndex);
                d.popupInfo.offsetX = Math.min(this.viewport.width - this.margin.right - width, Math.max(-this.margin.left, x)) - x;
                return manipulation.translate(x + d.popupInfo.offsetX, y);
            });

        let tooltipRect: Selection<any> = tooltipRootMerged.selectAll(Visual.TooltipRect.selectorName).data(d => [d]);   
        let tooltipRectMerged = tooltipRect
            .enter()
            .append("path")
            .merge(tooltipRect);
        tooltipRectMerged
            .classed(Visual.TooltipRect.className, true)
            .attr("display", (d: DataPoint) => d.popupInfo ? "inherit" : "none")
            .style("fill", this.data.settings.popup.color)
            .style("stroke", this.data.settings.popup.stroke)
            .attr("d", (d: DataPoint) => {
                const firstPoint: PointXY = {
                    "x": -2,
                    "y": this.isHigherMiddle(d.y, d.groupIndex) ? (-1 * marginTop) : 0,
                };

                const points: PointXY[] = [
                    firstPoint,
                    {
                        "x": -2,
                        "y": this.isHigherMiddle(d.y, d.groupIndex) ? (-1 * (marginTop + height)) : height,
                    },
                    {
                        "x": width - 2,
                        "y": this.isHigherMiddle(d.y, d.groupIndex) ? (-1 * (marginTop + height)) : height,
                    },
                    {
                        "x": width - 2,
                        "y": this.isHigherMiddle(d.y, d.groupIndex) ? (-1 * marginTop) : 0,
                    },
                    firstPoint,
                ];

                return line(points);
            });

        let tooltipTriangle: Selection<any> = tooltipRootMerged.selectAll(Visual.TooltipTriangle.selectorName).data(d => [d]);
        let tooltipTriangleMerged = tooltipTriangle
            .enter()
            .append("path")
            .merge(tooltipTriangle);
        tooltipTriangleMerged.classed(Visual.TooltipTriangle.className, true);
        tooltipTriangleMerged
            .style("fill", this.data.settings.popup.color)
            .style("stroke", this.data.settings.popup.stroke)
            .attr("d", (d: DataPoint) => {
                let path = [
                    {
                        "x": width / 2 - 5 - d.popupInfo.offsetX,
                        "y": this.isHigherMiddle(d.y, d.groupIndex) ? (-1 * marginTop) : 0,
                    },
                    {
                        "x": width / 2 - d.popupInfo.offsetX,
                        "y": this.isHigherMiddle(d.y, d.groupIndex) ? (-1 * (marginTop - 5)) : -5,
                    },
                    {
                        "x": width / 2 + 5 - d.popupInfo.offsetX,
                        "y": this.isHigherMiddle(d.y, d.groupIndex) ? (-1 * marginTop) : 0,
                    },
                ];
                return line(<DataPoint[]>path);
            })
            .style("stroke-width", "1px");

        let tooltipLine: Selection<any> = tooltipRootMerged.selectAll(Visual.TooltipLine.selectorName).data(d => [d]);
        let tooltipLineMerged = tooltipLine.enter().append("path").merge(tooltipLine);
        tooltipLineMerged.classed(Visual.TooltipLine.className, true);
        tooltipLineMerged
            .style("fill", this.data.settings.popup.color)
            .style("stroke", this.data.settings.popup.stroke || this.data.settings.popup.color)
            .style("stroke-width", "1px")
            .attr("d", (d: DataPoint) => {
                let path = [
                    {
                        "x": width / 2 - d.popupInfo.offsetX,
                        "y": this.isHigherMiddle(d.y, d.groupIndex) ?
                            yScales[d.groupIndex](d.y) + tooltipShiftY(d.y, d.groupIndex) - d.eventSize :
                            yScales[d.groupIndex](d.y) - tooltipShiftY(d.y, d.groupIndex) + d.eventSize,
                    },
                    {
                        "x": width / 2 - d.popupInfo.offsetX,
                        "y": this.isHigherMiddle(d.y, d.groupIndex) ? (-1 * marginTop) : 0, // end
                    }];
                return line(<DataPoint[]>path);
            });

        let timeRect: Selection<any> = tooltipRootMerged.selectAll(Visual.TooltipTimeRect.selectorName).data(d => [d]);
        let timeRectMerged = timeRect.enter().append("path").merge(timeRect);
        timeRectMerged.classed(Visual.TooltipTimeRect.className, true);
        timeRectMerged
            .style("fill", this.data.settings.popup.timeFill)
            .style("stroke", this.data.settings.popup.stroke)
            .style("display", showTimeDisplayProperty)
            .attr("d", (d: DataPoint) => {
                let path = [
                    {
                        "x": width - this.data.widthOfTooltipValueLabel - 2,
                        "y": this.isHigherMiddle(d.y, d.groupIndex) ? (-1 * (marginTop + height)) : 0,
                    },
                    {
                        "x": width - this.data.widthOfTooltipValueLabel - 2,
                        "y": this.isHigherMiddle(d.y, d.groupIndex)
                            ? (-1 * (marginTop + height - Visual.DefaultTooltipSettings.timeHeight))
                            : Visual.DefaultTooltipSettings.timeHeight,
                    },
                    {
                        "x": width - 2,
                        "y": this.isHigherMiddle(d.y, d.groupIndex)
                            ? (-1 * (marginTop + height - Visual.DefaultTooltipSettings.timeHeight))
                            : Visual.DefaultTooltipSettings.timeHeight,
                    },
                    {
                        "x": width - 2,
                        "y": this.isHigherMiddle(d.y, d.groupIndex) ? (-1 * (marginTop + height)) : 0,
                    }
                ];
                return line(<DataPoint[]>path);
            });

        let time: Selection<any> = tooltipRootMerged.selectAll(Visual.TooltipTime.selectorName).data(d => [d]);
        let timeMerged = time.enter().append("text").merge(time);
        timeMerged.classed(Visual.TooltipTime.className, true);
        const timeFontStyles = Visual.CONVERT_TEXT_PROPERTIES_TO_STYLE(Visual.getPopupValueTextProperties());
        Visual.APPLY_TEXT_FONT_STYLES(timeMerged, timeFontStyles);

        timeMerged
            .style("display", showTimeDisplayProperty)
            .style("fill", this.data.settings.popup.timeColor)
            .attr("x", (d: DataPoint) => width - this.data.widthOfTooltipValueLabel)
            .attr("y", (d: DataPoint) => this.isHigherMiddle(d.y, d.groupIndex)
                ? (-1 * (marginTop + height - Visual.DefaultTooltipSettings.timeHeight + 3))
                : Visual.DefaultTooltipSettings.timeHeight - 3)
            .text((d: DataPoint) => textMeasurementService.getTailoredTextOrDefault(Visual.getPopupValueTextProperties(d.popupInfo.value.toString()), this.data.widthOfTooltipValueLabel));

        let title: Selection<any> = tooltipRootMerged.selectAll(Visual.TooltipTitle.selectorName).data(d => [d]);
        let titleMerged = title.enter().append("text").merge(title);
        titleMerged
            .classed(Visual.TooltipTitle.className, true);

        const titleFontStyles = Visual.CONVERT_TEXT_PROPERTIES_TO_STYLE(Visual.getPopupTitleTextProperties());
        Visual.APPLY_TEXT_FONT_STYLES(titleMerged, titleFontStyles);

        titleMerged
            .style("display", showTitleDisplayProperty)
            .style("fill", this.data.settings.popup.fontColor)
            .attr("x", (d: DataPoint) => Visual.PopupTextPadding)
            .attr("y", (d: DataPoint) =>
                (this.isHigherMiddle(d.y, d.groupIndex) ? (-1 * (marginTop + height - 12)) : 12) + Visual.PopupTextPadding)
            .text((d: DataPoint) => {
                if (!d.popupInfo) {
                    return "";
                }
                let maxWidth = width - Visual.PopupTextPadding * 2 -
                    (this.data.settings.popup.showTime ? (this.data.widthOfTooltipValueLabel - Visual.PopupTextPadding) : 0) - 10;
                return textMeasurementService.getTailoredTextOrDefault(Visual.getPopupTitleTextProperties(d.popupInfo.title), maxWidth);
            });

        let getDescriptionDimenstions = (d: DataPoint): ElementDimensions => {
            let shiftY: number = Visual.PopupTextPadding + this.data.settings.popup.fontSize;

            let descriptionYOffset: number = shiftY + Visual.DefaultTooltipSettings.timeHeight;
            if (d.popupInfo) {
                shiftY = ((showTitleDisplayProperty && d.popupInfo.title) || (showTimeDisplayProperty && d.popupInfo.value)) ? descriptionYOffset : shiftY;
            }

            return {
                y: this.isHigherMiddle(d.y, d.groupIndex)
                    ? (-1 * (marginTop + height - shiftY))
                    : shiftY,
                x: Visual.PopupTextPadding,
                width: width - Visual.PopupTextPadding * 2,
                height: height - shiftY,
            };
        };

        let description: Selection<any> = tooltipRootMerged.selectAll(Visual.TooltipDescription.selectorName).data(d => [d]);
        let descriptionMerged = description.enter().append("text").merge(description);
        descriptionMerged.classed(Visual.TooltipDescription.className, true);
        const descriptionFontStyles = Visual.CONVERT_TEXT_PROPERTIES_TO_STYLE(Visual.getPopupDescriptionTextProperties(null, this.data.settings.popup.fontSize));
        Visual.APPLY_TEXT_FONT_STYLES(descriptionMerged, descriptionFontStyles);

        descriptionMerged
            .style("fill", this.data.settings.popup.fontColor)
            .text((d: DataPoint) => d.popupInfo && d.popupInfo.description)
            .each(function (series: Series) {
                let node = <SVGTextElement>this;
                const allowedWidth = width - 2 - Visual.PopupTextPadding * 2;
                const allowedHeight = height - Visual.DefaultTooltipSettings.timeHeight - Visual.PopupTextPadding * 2;
                textMeasurementService.wordBreak(node, allowedWidth, allowedHeight);
            })
            .attr("transform", (d: DataPoint) => {
                let descriptionDimenstions: ElementDimensions = getDescriptionDimenstions(d);
                return manipulation.translate(0, descriptionDimenstions.y);
            });
        descriptionMerged.selectAll("tspan").attr("x", Visual.PopupTextPadding);

        tooltipRect
            .exit()
            .remove();

        tooltipRoot
            .exit()
            .remove();
    }