public async render()

in src/UXClient/Components/ModelSearch/ModelSearch.ts [38:278]


  public async render(hierarchyData: any, chartOptions?: any) {
    this.chartOptions.setOptions(chartOptions);
    let self = this;
    let continuationToken, searchText;
    let targetElement = d3.select(this.renderTarget);
    targetElement.html("");
    this.wrapper = targetElement
      .append("div")
      .attr("class", "tsi-modelSearchWrapper");
    super.themify(this.wrapper, this.chartOptions.theme);
    let inputWrapper = this.wrapper
      .append("div")
      .attr("class", "tsi-modelSearchInputWrapper");

    let autocompleteOnInput = (st, event) => {
      self.usedContinuationTokens = {};

      // blow results away if no text
      if (st.length === 0) {
        searchText = st;
        self.instanceResults.html("");
        self.currentResultIndex = -1;
        (hierarchyElement.node() as any).style.display = "block";
        (showMore.node() as any).style.display = "none";
        noResults.style("display", "none");
      } else if (event.which === 13 || event.keyCode === 13) {
        (hierarchyElement.node() as any).style.display = "none";
        self.instanceResults.html("");
        self.currentResultIndex = -1;
        noResults.style("display", "none");
        searchInstances(st);
        searchText = st;
      }
    };

    let modelAutocomplete = new ModelAutocomplete(
      inputWrapper.node(),
      this.delegate
    );
    modelAutocomplete.render({
      onInput: autocompleteOnInput,
      onKeydown: (event, ap) => {
        this.handleKeydown(event, ap);
      },
      ...chartOptions,
    });
    var ap = modelAutocomplete.ap;

    let results = this.wrapper
      .append("div")
      .attr("class", "tsi-modelSearchResults")
      .on("scroll", function () {
        self.closeContextMenu();
        let that = this as any;
        if (
          that.scrollTop + that.clientHeight + 150 >
            (self.instanceResults.node() as any).clientHeight &&
          searchText.length !== 0
        ) {
          searchInstances(searchText, continuationToken);
        }
      });
    let noResults = results
      .append("div")
      .text(this.getString("No results"))
      .classed("tsi-noResults", true)
      .style("display", "none");
    let instanceResultsWrapper = results
      .append("div")
      .attr("class", "tsi-modelSearchInstancesWrapper");
    this.instanceResults = instanceResultsWrapper
      .append("div")
      .attr("class", "tsi-modelSearchInstances");
    let showMore = instanceResultsWrapper
      .append("div")
      .attr("class", "tsi-showMore")
      .text(this.getString("Show more") + "...")
      .on("click", () => searchInstances(searchText, continuationToken))
      .style("display", "none");

    let hierarchyElement = this.wrapper
      .append("div")
      .attr("class", "tsi-hierarchyWrapper");
    let hierarchy = new Hierarchy(hierarchyElement.node() as any);
    hierarchy.render(hierarchyData, {
      ...this.chartOptions,
      withContextMenu: true,
    });

    let searchInstances = async (searchText, ct = null) => {
      var self = this;
      if (ct === "END") return;
      if (ct === null || !self.usedContinuationTokens[ct]) {
        self.usedContinuationTokens[ct] = true;
        const r = await this.delegate.getInstancesSearch(searchText);
        let instances = r.instances.hits;
        //ontinuationToken = r.instances.hits.continuationToken;

        if (!continuationToken) continuationToken = "END";
        (showMore.node() as any).style.display =
          continuationToken !== "END" ? "block" : "none";
        if (instances.length == 0) {
          noResults.style("display", "block");
        } else {
          noResults.style("display", "none");
        }

        instances.forEach((i) => {
          let handleClick = (
            elt,
            wrapperMousePos,
            eltMousePos,
            fromKeyboard = false
          ) => {
            self.closeContextMenu();
            if (self.clickedInstance != elt) {
              self.clickedInstance = elt;

              (i as any).type = self.types.filter((t) => {
                return (
                  t.name.replace(/\s/g, "") ===
                  i.highlights.typeName
                    .split("<hit>")
                    .join("")
                    .split("</hit>")
                    .join("")
                    .replace(/\s/g, "")
                );
              })[0];
              let contextMenuActions = self.chartOptions.onInstanceClick(i);
              self.contextMenu = self.wrapper.append("div");
              if (!Array.isArray(contextMenuActions)) {
                contextMenuActions = [contextMenuActions];
              }
              let totalActionCount = contextMenuActions
                .map((cma) => Object.keys(cma).length)
                .reduce((p, c) => p + c, 0);
              let currentActionIndex = 0;
              contextMenuActions.forEach((cma, cmaGroupIdx) => {
                Object.keys(cma).forEach((k, kIdx, kArray) => {
                  let localActionIndex = currentActionIndex;
                  self.contextMenu
                    .append("div")
                    .text(k)
                    .on("click", cma[k])
                    .on("keydown", function () {
                      let evt = d3.event;
                      if (evt.keyCode === 13) {
                        this.click();
                      }
                      if (evt.keyCode === 13 || evt.keyCode === 37) {
                        self.closeContextMenu();
                        let results = self.instanceResults.selectAll(
                          ".tsi-modelResultWrapper"
                        );
                        results.nodes()[self.currentResultIndex].focus();
                      }
                      if (
                        evt.keyCode === 40 &&
                        localActionIndex + 1 < totalActionCount
                      ) {
                        // down
                        self.contextMenu
                          .node()
                          .children[
                            localActionIndex +
                              1 +
                              cmaGroupIdx +
                              (kIdx === kArray.length - 1 ? 1 : 0)
                          ].focus();
                      }
                      if (evt.keyCode === 38 && localActionIndex > 0) {
                        // up
                        self.contextMenu
                          .node()
                          .children[
                            localActionIndex -
                              1 +
                              cmaGroupIdx -
                              (kIdx === 0 ? 1 : 0)
                          ].focus();
                      }
                    })
                    .attr("tabindex", "0");
                  currentActionIndex++;
                });
                self.contextMenu.append("div").classed("tsi-break", true);
              });
              self.contextMenu.attr(
                "style",
                () => `top: ${wrapperMousePos - eltMousePos}px`
              );
              self.contextMenu.classed("tsi-modelSearchContextMenu", true);
              d3.select(elt).classed("tsi-resultSelected", true);
              if (self.contextMenu.node().children.length > 0 && fromKeyboard) {
                self.contextMenu.node().children[0].focus();
              }
            } else {
              self.clickedInstance = null;
            }
          };
          this.instanceResults
            .append("div")
            .html(self.getInstanceHtml(i)) // known unsafe usage of .html
            .on("click", function () {
              let mouseWrapper = d3.mouse(self.wrapper.node());
              let mouseElt = d3.mouse(this as any);
              handleClick(this, mouseWrapper[1], mouseElt[1]);
            })
            .on("keydown", () => {
              let evt = d3.event;
              if (evt.keyCode === 13) {
                let resultsNodes = this.instanceResults
                  .selectAll(".tsi-modelResultWrapper")
                  .nodes();
                let height = 0;
                for (var i = 0; i < this.currentResultIndex; i++) {
                  height += resultsNodes[0].clientHeight;
                }
                handleClick(
                  this.instanceResults
                    .select(".tsi-modelResultWrapper:focus")
                    .node(),
                  height - results.node().scrollTop + 48,
                  0,
                  true
                );
              }
              self.handleKeydown(evt, ap);
            })
            .attr("tabindex", "0")
            .classed("tsi-modelResultWrapper", true);
        });
      }
    };

    this.hierarchies = await this.delegate.getHierarchies();

    // get types
    this.types = await this.delegate.getTimeSeriesTypes();
  }