@matdata/yasgui-graph-plugin 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -15
- package/dist/yasgui-graph-plugin.cjs.js +176 -1
- package/dist/yasgui-graph-plugin.cjs.js.map +3 -3
- package/dist/yasgui-graph-plugin.esm.js +176 -1
- package/dist/yasgui-graph-plugin.esm.js.map +3 -3
- package/dist/yasgui-graph-plugin.min.js +18 -18
- package/dist/yasgui-graph-plugin.min.js.map +3 -3
- package/package.json +1 -1
|
@@ -116,6 +116,40 @@ function getDefaultNetworkOptions(themeColors, settings) {
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
// src/parsers.ts
|
|
119
|
+
async function parseBackgroundQueryResponse(response) {
|
|
120
|
+
var _a, _b;
|
|
121
|
+
if (!response) return [];
|
|
122
|
+
try {
|
|
123
|
+
let data2;
|
|
124
|
+
if (typeof response.json === "function") {
|
|
125
|
+
data2 = await response.json();
|
|
126
|
+
} else if (typeof response.data === "string") {
|
|
127
|
+
data2 = JSON.parse(response.data);
|
|
128
|
+
} else if (typeof response === "object") {
|
|
129
|
+
data2 = response;
|
|
130
|
+
} else {
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
const bindings = (_b = (_a = data2 == null ? void 0 : data2.results) == null ? void 0 : _a.bindings) != null ? _b : [];
|
|
134
|
+
const triples = [];
|
|
135
|
+
for (const binding of bindings) {
|
|
136
|
+
if (!binding.subject || !binding.predicate || !binding.object || typeof binding.subject.value !== "string" || typeof binding.predicate.value !== "string" || typeof binding.object.value !== "string") continue;
|
|
137
|
+
triples.push({
|
|
138
|
+
subject: binding.subject.value,
|
|
139
|
+
predicate: binding.predicate.value,
|
|
140
|
+
object: {
|
|
141
|
+
value: binding.object.value,
|
|
142
|
+
type: binding.object.type || "uri",
|
|
143
|
+
datatype: binding.object.datatype,
|
|
144
|
+
lang: binding.object["xml:lang"]
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return triples;
|
|
149
|
+
} catch (e) {
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
119
153
|
function parseConstructResults(yasrResults) {
|
|
120
154
|
const triples = [];
|
|
121
155
|
if (!yasrResults || !yasrResults.getBindings) {
|
|
@@ -29988,10 +30022,16 @@ function saveSettings(settings) {
|
|
|
29988
30022
|
}
|
|
29989
30023
|
|
|
29990
30024
|
// src/GraphPlugin.ts
|
|
30025
|
+
var LOADING_BORDER_WIDTH = 4;
|
|
30026
|
+
var LOADING_BORDER_COLOR = "#ffa500";
|
|
30027
|
+
var EXPANDED_BORDER_WIDTH = 3;
|
|
30028
|
+
var DEFAULT_BORDER_WIDTH = 2;
|
|
29991
30029
|
var GraphPlugin = class {
|
|
29992
30030
|
constructor(yasr) {
|
|
29993
30031
|
this.settingsPanelOpen = false;
|
|
29994
30032
|
this.clickOutsideHandler = null;
|
|
30033
|
+
this.expansionAbortController = null;
|
|
30034
|
+
this.uriToNodeId = /* @__PURE__ */ new Map();
|
|
29995
30035
|
this.yasr = yasr;
|
|
29996
30036
|
this.network = null;
|
|
29997
30037
|
this.currentTheme = getCurrentTheme();
|
|
@@ -30015,6 +30055,12 @@ var GraphPlugin = class {
|
|
|
30015
30055
|
static get label() {
|
|
30016
30056
|
return "Graph";
|
|
30017
30057
|
}
|
|
30058
|
+
/**
|
|
30059
|
+
* Help/documentation URL
|
|
30060
|
+
*/
|
|
30061
|
+
static get helpReference() {
|
|
30062
|
+
return "https://yasgui-doc.matdata.eu/docs/user-guide#graph-plugin";
|
|
30063
|
+
}
|
|
30018
30064
|
/**
|
|
30019
30065
|
* Check if plugin can handle the current results
|
|
30020
30066
|
* @returns True if results are from CONSTRUCT or DESCRIBE query
|
|
@@ -30036,6 +30082,12 @@ var GraphPlugin = class {
|
|
|
30036
30082
|
*/
|
|
30037
30083
|
draw() {
|
|
30038
30084
|
const wasPanelOpen = this.settingsPanelOpen;
|
|
30085
|
+
if (this.expansionAbortController) {
|
|
30086
|
+
this.expansionAbortController.abort();
|
|
30087
|
+
this.expansionAbortController = null;
|
|
30088
|
+
}
|
|
30089
|
+
this.expandedNodes = /* @__PURE__ */ new Set();
|
|
30090
|
+
this.uriToNodeId = /* @__PURE__ */ new Map();
|
|
30039
30091
|
this.yasr.resultsEl.innerHTML = "";
|
|
30040
30092
|
try {
|
|
30041
30093
|
this.triples = parseConstructResults(this.yasr.results);
|
|
@@ -30092,6 +30144,13 @@ var GraphPlugin = class {
|
|
|
30092
30144
|
this.nodesDataSet.update(updates);
|
|
30093
30145
|
}
|
|
30094
30146
|
});
|
|
30147
|
+
this.setupNodeExpansion();
|
|
30148
|
+
this.uriToNodeId = /* @__PURE__ */ new Map();
|
|
30149
|
+
this.nodesDataSet.get().forEach((node) => {
|
|
30150
|
+
if (node.uri) {
|
|
30151
|
+
this.uriToNodeId.set(node.uri, node.id);
|
|
30152
|
+
}
|
|
30153
|
+
});
|
|
30095
30154
|
if (!this.themeObserver) {
|
|
30096
30155
|
this.themeObserver = watchThemeChanges((newTheme) => {
|
|
30097
30156
|
this.applyTheme(newTheme);
|
|
@@ -30474,7 +30533,119 @@ var GraphPlugin = class {
|
|
|
30474
30533
|
return panel;
|
|
30475
30534
|
}
|
|
30476
30535
|
/**
|
|
30477
|
-
*
|
|
30536
|
+
* Setup double-click handler for node expansion
|
|
30537
|
+
*/
|
|
30538
|
+
setupNodeExpansion() {
|
|
30539
|
+
if (!this.network) return;
|
|
30540
|
+
this.network.on("doubleClick", (params) => {
|
|
30541
|
+
if (params.nodes.length === 0) return;
|
|
30542
|
+
const nodeId = params.nodes[0];
|
|
30543
|
+
const node = this.nodesDataSet.get(nodeId);
|
|
30544
|
+
if (node && node.uri && !node.uri.startsWith("_:")) {
|
|
30545
|
+
this.expandNode(node.uri);
|
|
30546
|
+
}
|
|
30547
|
+
});
|
|
30548
|
+
}
|
|
30549
|
+
/**
|
|
30550
|
+
* Expand a node by executing a DESCRIBE query for the given URI and
|
|
30551
|
+
* merging the returned triples into the existing graph.
|
|
30552
|
+
* @param uri - URI of the node to expand
|
|
30553
|
+
*/
|
|
30554
|
+
async expandNode(uri) {
|
|
30555
|
+
if (!this.yasr.executeQuery) {
|
|
30556
|
+
console.warn("yasgui-graph-plugin: background query execution not available (yasr.executeQuery is not configured)");
|
|
30557
|
+
return;
|
|
30558
|
+
}
|
|
30559
|
+
if (!this.triples || !this.prefixMap) return;
|
|
30560
|
+
if (this.expansionAbortController) {
|
|
30561
|
+
this.expansionAbortController.abort();
|
|
30562
|
+
}
|
|
30563
|
+
const controller = new AbortController();
|
|
30564
|
+
this.expansionAbortController = controller;
|
|
30565
|
+
const nodeId = this.uriToNodeId.get(uri);
|
|
30566
|
+
let originalColor = void 0;
|
|
30567
|
+
let originalBorderWidth = void 0;
|
|
30568
|
+
if (nodeId !== void 0) {
|
|
30569
|
+
const node = this.nodesDataSet.get(nodeId);
|
|
30570
|
+
if (node) {
|
|
30571
|
+
originalColor = node.color;
|
|
30572
|
+
originalBorderWidth = node.borderWidth;
|
|
30573
|
+
}
|
|
30574
|
+
this.nodesDataSet.update({
|
|
30575
|
+
id: nodeId,
|
|
30576
|
+
borderWidth: LOADING_BORDER_WIDTH,
|
|
30577
|
+
color: typeof originalColor === "object" && originalColor !== null ? { ...originalColor, border: LOADING_BORDER_COLOR } : { border: LOADING_BORDER_COLOR, background: originalColor != null ? originalColor : void 0 }
|
|
30578
|
+
});
|
|
30579
|
+
}
|
|
30580
|
+
const restoreNode = (borderWidth) => {
|
|
30581
|
+
if (nodeId !== void 0) {
|
|
30582
|
+
this.nodesDataSet.update({ id: nodeId, borderWidth, color: originalColor });
|
|
30583
|
+
}
|
|
30584
|
+
};
|
|
30585
|
+
try {
|
|
30586
|
+
const response = await this.yasr.executeQuery(`DESCRIBE <${uri}>`, {
|
|
30587
|
+
acceptHeader: "application/sparql-results+json",
|
|
30588
|
+
signal: controller.signal
|
|
30589
|
+
});
|
|
30590
|
+
if (controller.signal.aborted) {
|
|
30591
|
+
restoreNode(originalBorderWidth != null ? originalBorderWidth : DEFAULT_BORDER_WIDTH);
|
|
30592
|
+
return;
|
|
30593
|
+
}
|
|
30594
|
+
const newTriples = await parseBackgroundQueryResponse(response);
|
|
30595
|
+
if (controller.signal.aborted) {
|
|
30596
|
+
restoreNode(originalBorderWidth != null ? originalBorderWidth : DEFAULT_BORDER_WIDTH);
|
|
30597
|
+
return;
|
|
30598
|
+
}
|
|
30599
|
+
const existingKeys = new Set(
|
|
30600
|
+
this.triples.map((t) => `${t.subject}|${t.predicate}|${t.object.value}`)
|
|
30601
|
+
);
|
|
30602
|
+
const uniqueNew = newTriples.filter(
|
|
30603
|
+
(t) => !existingKeys.has(`${t.subject}|${t.predicate}|${t.object.value}`)
|
|
30604
|
+
);
|
|
30605
|
+
if (uniqueNew.length > 0) {
|
|
30606
|
+
this.triples = [...this.triples, ...uniqueNew];
|
|
30607
|
+
this.mergeNewTriples();
|
|
30608
|
+
}
|
|
30609
|
+
restoreNode(EXPANDED_BORDER_WIDTH);
|
|
30610
|
+
} catch (error) {
|
|
30611
|
+
if ((error == null ? void 0 : error.name) === "AbortError") {
|
|
30612
|
+
restoreNode(originalBorderWidth != null ? originalBorderWidth : DEFAULT_BORDER_WIDTH);
|
|
30613
|
+
return;
|
|
30614
|
+
}
|
|
30615
|
+
console.error("yasgui-graph-plugin: error expanding node", uri, error);
|
|
30616
|
+
restoreNode(originalBorderWidth != null ? originalBorderWidth : DEFAULT_BORDER_WIDTH);
|
|
30617
|
+
}
|
|
30618
|
+
}
|
|
30619
|
+
/**
|
|
30620
|
+
* Incrementally add new triples to the vis-network DataSets without a full redraw.
|
|
30621
|
+
* New nodes and edges are added; existing ones are left untouched.
|
|
30622
|
+
* Expects `this.triples` to already include the new triples.
|
|
30623
|
+
*/
|
|
30624
|
+
mergeNewTriples() {
|
|
30625
|
+
if (!this.triples || !this.prefixMap || !this.nodesDataSet || !this.edgesDataSet) return;
|
|
30626
|
+
const themeColors = getThemeNodeColors(this.currentTheme);
|
|
30627
|
+
const { nodes, edges } = triplesToGraph(this.triples, this.prefixMap, themeColors, this.settings);
|
|
30628
|
+
const existingValues = new Set(this.nodesDataSet.get().map((n) => n.fullValue));
|
|
30629
|
+
const nodesToAdd = nodes.filter((n) => !existingValues.has(n.fullValue));
|
|
30630
|
+
const existingEdgeKeys = new Set(
|
|
30631
|
+
this.edgesDataSet.get().map((e) => `${e.from}|${e.predicate}|${e.to}`)
|
|
30632
|
+
);
|
|
30633
|
+
const edgesToAdd = edges.filter(
|
|
30634
|
+
(e) => !existingEdgeKeys.has(`${e.from}|${e.predicate}|${e.to}`)
|
|
30635
|
+
);
|
|
30636
|
+
if (nodesToAdd.length > 0) {
|
|
30637
|
+
this.nodesDataSet.add(nodesToAdd);
|
|
30638
|
+
nodesToAdd.forEach((n) => {
|
|
30639
|
+
if (n.uri != null) {
|
|
30640
|
+
this.uriToNodeId.set(n.uri, n.id);
|
|
30641
|
+
}
|
|
30642
|
+
});
|
|
30643
|
+
}
|
|
30644
|
+
if (edgesToAdd.length > 0) {
|
|
30645
|
+
this.edgesDataSet.add(edgesToAdd);
|
|
30646
|
+
}
|
|
30647
|
+
}
|
|
30648
|
+
/**
|
|
30478
30649
|
* @returns Icon element
|
|
30479
30650
|
*/
|
|
30480
30651
|
getIcon() {
|
|
@@ -30496,6 +30667,10 @@ var GraphPlugin = class {
|
|
|
30496
30667
|
*/
|
|
30497
30668
|
destroy() {
|
|
30498
30669
|
this.removeClickOutsideHandler();
|
|
30670
|
+
if (this.expansionAbortController) {
|
|
30671
|
+
this.expansionAbortController.abort();
|
|
30672
|
+
this.expansionAbortController = null;
|
|
30673
|
+
}
|
|
30499
30674
|
if (this.themeObserver) {
|
|
30500
30675
|
this.themeObserver.disconnect();
|
|
30501
30676
|
this.themeObserver = null;
|