sparnatural
Version:
Visual client-side SPARQL query builder and knowledge graph exploration tool
172 lines (139 loc) • 5.64 kB
text/typescript
import { DataFactory } from 'rdf-data-factory';
import { BgpPattern, Pattern, Triple, ValuePatternRow, ValuesPattern } from "sparqljs";
import { SelectedVal } from "../SelectedVal";
import SparqlFactory from "../../generators/sparql/SparqlFactory";
import { AbstractWidget, RDFTerm, RdfTermValue, ValueRepetition, WidgetValue } from "./AbstractWidget";
import EndClassGroup from "../builder-section/groupwrapper/criteriagroup/startendclassgroup/EndClassGroup";
import { AutocompleteDataProviderIfc, mergeDatasourceResults, NoOpAutocompleteProvider, RdfTermDatasourceItem } from "./data/DataProviders";
import Awesomplete from 'awesomplete';
import { I18n } from '../../settings/I18n';
import HTMLComponent from '../HtmlComponent';
const factory = new DataFactory();
export interface AutocompleteConfiguration {
dataProvider: AutocompleteDataProviderIfc,
maxItems: number
}
export class AutoCompleteWidget extends AbstractWidget {
// The default implementation of AutocompleteConfiguration
static defaultConfiguration: AutocompleteConfiguration = {
dataProvider: new NoOpAutocompleteProvider(),
maxItems:15
}
protected configuration: AutocompleteConfiguration;
constructor(
parentComponent: HTMLComponent,
configuration: AutocompleteConfiguration,
startClassValue: SelectedVal,
objectPropVal: SelectedVal,
endClassValue: SelectedVal
) {
super(
"autocomplete-widget",
parentComponent,
null,
startClassValue,
objectPropVal,
endClassValue,
ValueRepetition.MULTIPLE
);
this.configuration = configuration;
}
render() {
super.render();
let inputHtml = $(`<input class="awesomplete"/>`);
this.html.append(inputHtml);
let errorHtml =
$(`<div class="no-items" style="display: none; font-style:italic;">
${I18n.labels.ListWidgetNoItem}
</div>`);
// $( "#foo" )[ 0 ] is pulling the DOM element from the JQuery object
// see https://learn.jquery.com/using-jquery-core/faq/how-do-i-pull-a-native-dom-element-from-a-jquery-object/
const queryInput:HTMLElement = inputHtml[0];
const awesomplete = new Awesomplete(queryInput, {
filter: () => { // We will provide a list that is already filtered ...
return true;
},
sort: false, // ... and sorted.
minChars: 3,
maxItems: this.configuration.maxItems,
list: []
});
// the callback called when proposals have been fetched, to populate the suggestion list
let callback = (items:RdfTermDatasourceItem[]) => {
// find distinct values of the 'group' binding
const groups = [...new Set(items.map(item => item.group))];
let list = new Array<{label:String, value:String}>();
if(groups.length == 1 && groups[0] == undefined) {
// no groups defined at all
items.forEach(item => {
// Awesomplete list will contain the label as 'label', and the RDFTerm JSON serialization as 'value'
list.push({
label: item.label,
value: JSON.stringify(item.term)
});
});
} else {
// we have some groups, merge
let mergedResult = mergeDatasourceResults(items);
mergedResult.forEach(item => {
list.push({
label: (item.group)?"<span title='"+item.group+"'>"+item.label+"</span>":item.label,
value: JSON.stringify(item.term)
});
});
}
// toggle spinner
if(list.length == 0) {
this.toggleSpinner(I18n.labels.AutocompleteSpinner_NoResults);
} else {
this.toggleSpinner('')
}
// build final list
awesomplete.list = list;
awesomplete.evaluate();
}
let errorCallback = (payload:any) => {
this.toggleSpinner(I18n.labels.AutocompleteSpinner_NoResults);
}
// when user selects a value from the autocompletion list...
queryInput.addEventListener("awesomplete-selectcomplete", (event:Event) => {
// fetch the autocomplete event payload, which is the JSON serialization of the RDFTerm
let awesompleteEvent:{label:string, value:string} = (event as unknown as {text:{label:string, value:string}}).text;
let autocompleteValue= new RdfTermValue({
label: awesompleteEvent.label,
// parse back the RDFTerm as an object
rdfTerm: (JSON.parse(awesompleteEvent.value) as RDFTerm),
});
// set the value on the criteria
inputHtml.val(autocompleteValue.value.label);
this.triggerRenderWidgetVal(autocompleteValue);
});
// add the behavior on the input HTML element to fetch the autocompletion value
var autocompleteTimer = 0;
queryInput.addEventListener("input", (event:Event) => {
const phrase = (event.target as HTMLInputElement)?.value;
// Process inputText as you want, e.g. make an API request.
if(phrase.length >= 3) {
// cancel the previously-set timer
if (autocompleteTimer) {
window.clearTimeout(autocompleteTimer);
}
autocompleteTimer = window.setTimeout(() => {
this.toggleSpinner(I18n.labels.AutocompleteSpinner_Searching)
this.configuration.dataProvider.getAutocompleteSuggestions(
this.startClassVal.type,
this.objectPropVal.type,
this.endClassVal.type,
phrase,
callback,
errorCallback
);
},
350
);
}
});
return this;
}
parseInput(input: RdfTermValue["value"]): RdfTermValue {return new RdfTermValue(input)}
}