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();
}