public draw()

in src/UXClient/Components/Legend/Legend.ts [375:548]


	public draw(legendState: string, chartComponentData, labelMouseover, svgSelection, options, labelMouseoutAction = null, stickySeriesAction = null) {
        this.chartOptions.setOptions(options);
        this.chartComponentData = chartComponentData;
        this.legendState = legendState;
        this.stickySeriesAction = stickySeriesAction;
        this.labelMouseover = labelMouseover;
        this.labelMouseout = this.labelMouseoutWrapper(labelMouseoutAction, svgSelection);
        this.svgSelection = svgSelection;
        var legend = this.legendElement;
        var self = this;

        
        super.themify(this.legendElement, this.chartOptions.theme);

        legend.style('visibility', this.legendState != 'hidden')
            .classed('compact', this.legendState == 'compact')
            .classed('hidden', this.legendState == 'hidden');

        let seriesNames = Object.keys(this.chartComponentData.displayState);
        var seriesLabels: any = legend.selectAll(".tsi-seriesLabel")
            .data(seriesNames, d => d);

        var seriesLabelsEntered = seriesLabels.enter()
            .append("div") 
            .merge(seriesLabels)
            .attr("class", (d, i) => {
                return "tsi-seriesLabel " + (this.chartComponentData.displayState[d]["visible"] ? " shown" : "");
            })
            .style("min-width", () => {
                return Math.min(124, this.legendElement.node().clientWidth / seriesNames.length) + 'px';  
            })
            .style("border-color", function (d, i) {
                if (d3.select(this).classed("shown"))
                    return self.chartComponentData.displayState[d].color;
                return "lightgray";
            });

        var self = this;

        const heightPerNameLabel: number = 25;
        const verticalPaddingPerSeriesLabel: number = 16;
        const usableLegendHeight: number = legend.node().clientHeight;
        var prospectiveAggregateHeight = Math.ceil(Math.max(201, (usableLegendHeight / seriesLabelsEntered.size())));
        var contentHeight = 0;

        seriesLabelsEntered.each(function (aggKey: string, i: number) {
            let heightPerSplitBy = self.getHeightPerSplitBy(aggKey);
            var splitByLabelData = Object.keys(self.chartComponentData.timeArrays[aggKey]);
            var noSplitBys: boolean = splitByLabelData.length == 1 && splitByLabelData[0] == "";
            var seriesNameLabel = d3.select(this).selectAll(".tsi-seriesNameLabel").data([aggKey]);
            d3.select(this).classed('tsi-nsb', noSplitBys);
            var enteredSeriesNameLabel = seriesNameLabel.enter().append("button")
                .merge(seriesNameLabel as d3.Selection<HTMLButtonElement, string, any, unknown>)
                .attr("class", (agg: string, i) => {
                    return "tsi-seriesNameLabel" + (self.chartComponentData.displayState[agg].visible ? " shown" : "");
                }) 
                .attr("aria-label", (agg: string) => {
                    let showOrHide = self.chartComponentData.displayState[agg].visible ? self.getString('hide group') : self.getString('show group');
                    return `${showOrHide} ${self.getString('group')} ${Utils.stripNullGuid(self.chartComponentData.displayState[agg].name)}`;
                })   
                .on("click", function (d: string, i: number) {
                    var newState = !self.chartComponentData.displayState[d].visible;
                    self.chartComponentData.displayState[d].visible = newState;

                    //turn off sticky if making invisible
                    if (newState == false && (self.chartComponentData.stickiedKey != null && 
                        self.chartComponentData.stickiedKey.aggregateKey == d)) {
                        self.chartComponentData.stickiedKey = null;
                    }
                    self.drawChart();
                })
                .on("mouseover", (d) => {
                    labelMouseover(d);
                })
                .on("mouseout", (d) => {
                    self.labelMouseout(svgSelection, d);
                });
            let dataType = self.chartComponentData.displayState[aggKey].dataType;
            if (dataType === DataTypes.Categorical || dataType === DataTypes.Events) {
                enteredSeriesNameLabel.classed('tsi-nonCompactNonNumeric', true);
                let colorKey = enteredSeriesNameLabel.selectAll('.tsi-colorKey').data(['']);
                let colorKeyEntered = colorKey.enter()
                    .append("div")
                    .attr("class", 'tsi-colorKey')
                    .merge(colorKey as d3.Selection<HTMLDivElement,string,HTMLButtonElement,string>);
                self.createNonNumericColorKey(dataType, colorKeyEntered, aggKey);
                colorKey.exit().remove();
            }

            var seriesNameLabelText = enteredSeriesNameLabel.selectAll("h4").data([aggKey]);
            seriesNameLabelText.enter()
                .append("h4")
                .merge(seriesNameLabelText as d3.Selection<HTMLHeadingElement,string,HTMLButtonElement,string>)
                .attr("title", (d: string) => Utils.stripNullGuid(self.chartComponentData.displayState[d].name))
                .each(function() {
                    Utils.appendFormattedElementsFromString(d3.select(this), self.chartComponentData.displayState[aggKey].name);
                });
            

            seriesNameLabelText.exit().remove();
            seriesNameLabel.exit().remove();

            var splitByContainerHeight;
            if (splitByLabelData.length > (prospectiveAggregateHeight / heightPerSplitBy)) {
                splitByContainerHeight = prospectiveAggregateHeight - heightPerNameLabel;
                contentHeight += splitByContainerHeight + heightPerNameLabel;
            } else if (splitByLabelData.length > 1 || (splitByLabelData.length === 1 && splitByLabelData[0] !== "")) {
                splitByContainerHeight = splitByLabelData.length * heightPerSplitBy + heightPerNameLabel;
                contentHeight += splitByContainerHeight + heightPerNameLabel;
            } else {
                splitByContainerHeight = heightPerSplitBy;
                contentHeight += splitByContainerHeight;
            }
            if (self.chartOptions.legend == "shown") {
                d3.select(this).style("height", splitByContainerHeight + "px");
            } else {
                d3.select(this).style("height", "unset");
            }

            var splitByContainer = d3.select(this).selectAll(".tsi-splitByContainer").data([aggKey]);
            var splitByContainerEntered = splitByContainer.enter().append("div")
                .merge(splitByContainer as d3.Selection<HTMLDivElement,string,any,unknown>)
                .classed("tsi-splitByContainer", true);

            let aggSelection = d3.select(this);
            
            var sBs = self.renderSplitBys(aggKey, aggSelection, dataType, noSplitBys);
            splitByContainerEntered.on("scroll", function () {
                if (self.chartOptions.legend == "shown") {
                    if ((<any>this).scrollTop + (<any>this).clientHeight + 40 > (<any>this).scrollHeight) {
                        const oldShownSplitBys = self.chartComponentData.displayState[aggKey].shownSplitBys; 
                        self.chartComponentData.displayState[aggKey].shownSplitBys = Math.min(oldShownSplitBys + 20, splitByLabelData.length);
                        if (oldShownSplitBys != self.chartComponentData.displayState[aggKey].shownSplitBys) {
                            self.renderSplitBys(aggKey, aggSelection, dataType, noSplitBys);
                        }
                    }    
                }
            });
            d3.select(this).on('scroll', function () {
                if (self.chartOptions.legend == "compact") {
                    if ((<any>this).scrollLeft + (<any>this).clientWidth + 40 > (<any>this).scrollWidth) {
                        const oldShownSplitBys = self.chartComponentData.displayState[aggKey].shownSplitBys; 
                        self.chartComponentData.displayState[aggKey].shownSplitBys = Math.min(oldShownSplitBys + 20, splitByLabelData.length);
                        if (oldShownSplitBys != self.chartComponentData.displayState[aggKey].shownSplitBys) {
                            this.renderSplitBys(dataType);                   
                        }
                    }    
                }
            });
            splitByContainer.exit().remove();

        });

        if (this.chartOptions.legend == 'shown') {
            var legendHeight = legend.node().clientHeight;
            //minSplitBysForFlexGrow: the minimum number of split bys for flex-grow to be triggered 
            if (contentHeight < usableLegendHeight) {
                this.legendElement.classed("tsi-flexLegend", true);
                seriesLabelsEntered.each(function (d) {
                    let heightPerSplitBy = self.getHeightPerSplitBy(d);
                    var minSplitByForFlexGrow = (prospectiveAggregateHeight - heightPerNameLabel) / heightPerSplitBy;

                    var splitBysCount = Object.keys(self.chartComponentData.displayState[String(d3.select(this).data()[0])].splitBys).length;
                    if (splitBysCount > minSplitByForFlexGrow) {
                        d3.select(this).style("flex-grow", 1);
                    }
                });
            } else {
                this.legendElement.classed("tsi-flexLegend", false);
            }
        }

        seriesLabels.exit().remove();
	}