sparnatural
Version:
Visual client-side SPARQL query builder and knowledge graph exploration tool
346 lines (299 loc) • 10.5 kB
text/typescript
import { BgpPattern, Pattern, Triple, ValuesPattern } from "sparqljs";
import UiuxConfig from "../../IconsConstants";
import { SelectedVal } from "../../SelectedVal";
import { AbstractWidget, RDFTerm, RdfTermValue, ValueRepetition, WidgetValue } from "../AbstractWidget";
import "jstree"
import ISettings from "../../../../sparnatural/settings/ISettings";
import { ValuePatternRow } from "sparqljs";
import EndClassGroup from "../../builder-section/groupwrapper/criteriagroup/startendclassgroup/EndClassGroup";
import SparqlFactory from "../../../generators/sparql/SparqlFactory";
import { DataFactory } from 'rdf-data-factory';
import { I18n } from "../../../settings/I18n";
import { NoOpTreeDataProvider, RdfTermTreeDatasourceItem, TreeDataProviderIfc } from "../data/DataProviders";
import HTMLComponent from "../../HtmlComponent";
const factory = new DataFactory();
require("jstree/dist/themes/default/style.min.css");
export interface TreeConfiguration {
dataProvider: TreeDataProviderIfc,
maxSelectedItems: number
}
export class TreeWidget extends AbstractWidget {
// The default implementation of TreeConfiguration
static defaultConfiguration: TreeConfiguration = {
dataProvider: new NoOpTreeDataProvider(),
maxSelectedItems: 3
}
configuration:TreeConfiguration;
IdCriteriaGroupe: any;
jsTree: any;
value: RdfTermValue;
// html content
button: any;
hiddenInput: any;
startClassVal: SelectedVal;
objectPropVal: SelectedVal;
endClassVal: SelectedVal;
displayLayer: JQuery<HTMLElement>
constructor(
parentComponent: HTMLComponent,
configuration:TreeConfiguration,
startClassVal: SelectedVal,
objectPropVal: SelectedVal,
endClassVal: SelectedVal
) {
super(
"tree-widget",
parentComponent,
null,
startClassVal,
objectPropVal,
endClassVal,
ValueRepetition.MULTIPLE
);
this.configuration = configuration;
this.IdCriteriaGroupe = "id";
this.startClassVal = startClassVal;
this.endClassVal = endClassVal;
this.objectPropVal = objectPropVal;
}
render() {
super.render();
this.button = $(
'<a id="ecgrw-' +
this.IdCriteriaGroupe +
'-input" class="treeBtnDisplay">' +
UiuxConfig.ICON_TREE +
"</a>"
);
this.hiddenInput = $(
'<input id="ecgrw-' +
this.IdCriteriaGroupe +
'-input-value" type="hidden"/>'
);
this.displayLayer = $(
'<div id="ecgrw-' +
this.IdCriteriaGroupe +
`-displayLayer" class="treeLayer"><div class="treeClose">${UiuxConfig.ICON_REG_XMARK}</div><div class="treeNotice"></div><div class="treeDisplay" id="ecgrw-` +
this.IdCriteriaGroupe +
'-display"></div><div class="treeActions"><a class="treeCancel">' +
I18n.labels.TreeWidgetDelete +
'</a><a class="treeSubmit">' +
I18n.labels.TreeWidgetSelect +
"</a></div></div>"
);
this.html
.append(this.button)
.append(this.hiddenInput)
.append(this.displayLayer);
//render this element
var startClassGroup_value = this.startClassVal.type;
var endClassGroup_value = this.endClassVal.type;
var ObjectPropertyGroup_value = this.objectPropVal.type;
var self = this;
let dataProvider = this.configuration.dataProvider;
var options = {
core: {
multiple: true,
data: function (
node: { id: string },
callback: {
call: (arg0: any, arg1: { id: any; text: any }[]) => void;
}
) {
let nodeCallback:(items:RdfTermTreeDatasourceItem[]) => void = function(
items:RdfTermTreeDatasourceItem[]
) {
var result = [];
for (var i = 0; i < items.length; i++) {
var text = items[i].label;
// shorten the label if too long to avoid tree goind far right
if(text.length > 90) {
text = text.substring(0,90)+" (...)";
}
var aNode: {
id: string;
text: string;
children?: boolean;
state?: { disabled: boolean };
parent?: any;
} = {
id: items[i].term.value,
text: text,
};
if (items[i].hasChildren) {
aNode.children = true;
}
if (items[i].disabled) {
aNode.state = {
disabled: true, // node disabled
};
}
aNode.parent = node.id;
result.push(aNode);
}
callback.call(this, result);
if (node.id === "#") {
self.onTreeDataLoaded(result);
}
}
// TODO : this is not working for now
let errorCallback = (payload:any) => {
console.error(payload)
}
if(node.id === "#") {
dataProvider.getRoots(
startClassGroup_value,
ObjectPropertyGroup_value,
endClassGroup_value,
nodeCallback,
errorCallback
);
} else {
dataProvider.getChildren(
node.id,
startClassGroup_value,
ObjectPropertyGroup_value,
endClassGroup_value,
nodeCallback,
errorCallback
);
}
},
themes: {
icons: false,
},
},
checkbox: {
keep_selected_style: false,
three_state: false,
cascade: "down+undetermined",
cascade_to_disabled: true,
},
plugins: ["changed", "wholerow", "checkbox"],
};
// this.jsTree = $("#ecgrw-" + id_inputs + "-display").jstree(options);
this.jsTree = this.displayLayer.find("#ecgrw-"+this.IdCriteriaGroupe+"-display").jstree(options);
this.button.on("click", { arg1: this }, this.onClickDisplay);
//disable/enable on max selction
this.jsTree.on("changed.jstree", { arg1: this }, this.onChangedJstree);
this.jsTree.on("after_open.jstree", { arg1: this }, this.onChangedJstree);
this.displayLayer
.find(".treeSubmit")
.on("click", { arg1: this }, this.onClickSelect);
this.displayLayer
.find(".treeCancel")
.on("click", { arg1: this }, this.onClickCancel);
this.displayLayer
.find(".treeClose")
.on("click", { arg1: this }, this.onClickClose);
this.displayLayer.hide();
return this;
}
onTreeDataLoaded = function onTreeDataLoaded(result: string | any[]) {
if (result.length == 0) {
$("#ecgrw-" + this.IdCriteriaGroupe + "-displayLayer .treeNotice")
.text(I18n.labels.TreeWidgetNoData)
.show();
} else {
$("#ecgrw-" + this.IdCriteriaGroupe + "-displayLayer .treeNotice").hide();
}
};
//limit to 3 selction
onChangedJstree = function (e: { data: { arg1: any } }, data: any) {
let this_:TreeWidget = e.data.arg1;
var items = $(this_.jsTree).find("li.jstree-node");
var selecteds = this_.jsTree.jstree().get_top_checked();
for (var i = 0; i < items.length; i++) {
var id = $(items[i]).attr("id");
if (selecteds.indexOf(id) > -1) {
$(items[i]).addClass("tree-item-selected");
} else {
$(items[i]).removeClass("tree-item-selected");
}
if ($(items[i]).parents(".tree-item-selected").length > 0) {
var node = this_.jsTree.jstree(true).get_node(id);
if (!this_.jsTree.jstree(true).is_disabled(node)) {
$(items[i]).addClass("is-reactivable");
this_.jsTree.jstree(true).disable_checkbox(node);
this_.jsTree.jstree(true).disable_node(node);
}
} else {
if ($(items[i]).hasClass("is-reactivable")) {
$(items[i]).addClass("red");
this_.jsTree.jstree("enable_checkbox", id);
this_.jsTree.jstree(true).enable_node(id);
}
}
}
if (this_.jsTree.jstree().get_top_checked().length >= this_.configuration.maxSelectedItems) {
for (var i = 0; i < items.length; i++) {
var id = $(items[i]).attr("id");
if (selecteds.indexOf(id) == -1) {
var node = this_.jsTree.jstree(true).get_node(id);
if (!this_.jsTree.jstree(true).is_disabled(node)) {
$(items[i]).addClass("is-reactivable");
this_.jsTree.jstree(true).disable_checkbox(node);
this_.jsTree.jstree(true).disable_node(node);
}
}
}
this_.jsTree.addClass("max-selected");
} else {
if (this_.jsTree.hasClass("max-selected")) {
var items = $(this_.jsTree).find("li.jstree-node");
for (var i = 0; i < items.length; i++) {
var id = $(items[i]).attr("id");
if (selecteds.indexOf(id) == -1) {
if ($(items[i]).hasClass("is-reactivable")) {
$(items[i]).addClass("red");
if ($(items[i]).parents(".tree-item-selected").length == 0) {
this_.jsTree.jstree("enable_checkbox", id);
this_.jsTree.jstree(true).enable_node(id);
}
}
}
}
this_.jsTree.removeClass("max-selected");
}
}
};
onClickDisplay = function (e: any) {
let this_:TreeWidget = e.data.arg1;
this_.displayLayer.show();
};
onClickCancel = function (e: any) {
let this_:TreeWidget = e.data.arg1;
this_.displayLayer.hide();
};
onClickSelect = function (e: any) {
let this_:TreeWidget = e.data.arg1;
const values = this_.getValue()
this_.displayLayer?.hide();
this_.triggerRenderWidgetVal(values)
};
onClickClose = function (e: any) {
let this_:TreeWidget = e.data.arg1;
this_.displayLayer?.hide();
$(this_.parentComponent).trigger("change");
};
getValue = function ():Array<RdfTermValue> {
var checked = this.jsTree.jstree().get_top_checked(true);
// rebuild a clean data structure
var values = [];
for (var node in checked) {
const val = new RdfTermValue({
// TODO : find a wat to retrieve the itemLabel
label: checked[node].original.text,
rdfTerm: {
type: "uri",
value: checked[node].id
}
});
values.push(val);
}
return values;
};
parseInput(input: RdfTermValue["value"]): RdfTermValue {
return new RdfTermValue(input);
}
}