@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
package/README.md
CHANGED
|
@@ -17,7 +17,8 @@ A YASGUI plugin for visualizing SPARQL CONSTRUCT and DESCRIBE query results as i
|
|
|
17
17
|
- **� Compact Mode**: Hide literal and class nodes; show rdf:type and datatype properties in enhanced tooltips instead
|
|
18
18
|
- **🔍 Navigation**: Mouse wheel zoom, drag to pan, "Zoom to Fit" button
|
|
19
19
|
- **✋ Drag & Drop**: Reorganize nodes by dragging them to new positions (nodes stay pinned after manual drag)
|
|
20
|
-
-
|
|
20
|
+
- **� Node Expansion**: Double-click any URI node to fetch and merge related triples via DESCRIBE queries (see [Expand Nodes with Double Click](#-expand-nodes-with-double-click))
|
|
21
|
+
- **�💬 Rich Tooltips**: Modern HTML tooltips with node type, value, namespace, and datatype information
|
|
21
22
|
- **🌓 Theme Support**: Automatic light/dark mode detection and dynamic color switching
|
|
22
23
|
- **⚡ Performance**: Handles up to 1,000 nodes with <2s render time
|
|
23
24
|
- **♿ Accessible**: WCAG AA color contrast, keyboard navigation support
|
|
@@ -105,6 +106,7 @@ After running the query, click the **"Graph"** tab to see the visualization.
|
|
|
105
106
|
|
|
106
107
|
### Interaction
|
|
107
108
|
- **Drag Nodes**: Click and drag any node to reposition it (nodes are automatically pinned in place after dragging)
|
|
109
|
+
- **Expand Nodes**: 🆕 Double-click any blue URI node to fetch and merge additional RDF triples for that resource (see [Node Expansion](#expand-nodes-with-double-click) below)
|
|
108
110
|
- **Tooltips**: Hover over nodes/edges to see rich HTML tooltips with type, value, namespace, and datatype information
|
|
109
111
|
|
|
110
112
|
### Understanding Colors
|
|
@@ -240,7 +242,100 @@ class CustomGraphPlugin extends GraphPlugin {
|
|
|
240
242
|
Yasgui.Yasr.registerPlugin('customGraph', CustomGraphPlugin);
|
|
241
243
|
```
|
|
242
244
|
|
|
243
|
-
##
|
|
245
|
+
## � Expand Nodes with Double Click
|
|
246
|
+
|
|
247
|
+
The graph plugin supports **interactive node expansion** via double-clicking. This allows you to progressively explore RDF graphs by fetching additional triples for any URI node without redrawing the entire graph.
|
|
248
|
+
|
|
249
|
+
### How It Works
|
|
250
|
+
|
|
251
|
+
1. **Double-click a blue URI node** in the graph
|
|
252
|
+
2. The node's border turns **orange and thickens** (loading state)
|
|
253
|
+
3. A `DESCRIBE <uri>` query is sent to the SPARQL endpoint
|
|
254
|
+
4. **New triples are merged** into the existing graph
|
|
255
|
+
5. Node's border returns to normal width with **thicker border (3px) to indicate expansion**
|
|
256
|
+
6. Graph layout and zoom level are **preserved**
|
|
257
|
+
|
|
258
|
+
### Visual Feedback
|
|
259
|
+
|
|
260
|
+
| State | Border | Meaning |
|
|
261
|
+
|-------|--------|---------|
|
|
262
|
+
| **Default** | 2px | Node has not been expanded |
|
|
263
|
+
| **Loading** | 4px, orange | DESCRIBE query in progress |
|
|
264
|
+
| **Expanded** | 3px, normal color | Successfully expanded |
|
|
265
|
+
|
|
266
|
+
### Supported Node Types
|
|
267
|
+
|
|
268
|
+
| Node Type | Can Expand? | Reason |
|
|
269
|
+
|-----------|-------|----|
|
|
270
|
+
| 🔵 **URI nodes** | ✅ Yes | DESCRIBE works on URIs |
|
|
271
|
+
| 🟢 **Literals** | ❌ No | Cannot run DESCRIBE on literal values |
|
|
272
|
+
| ⚪ **Blank nodes** | ❌ No | Blank nodes have no resolvable identity |
|
|
273
|
+
|
|
274
|
+
### Example: Exploring a Knowledge Graph
|
|
275
|
+
|
|
276
|
+
**Initial Query**:
|
|
277
|
+
```sparql
|
|
278
|
+
PREFIX ex: <http://example.org/>
|
|
279
|
+
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
|
|
280
|
+
|
|
281
|
+
CONSTRUCT {
|
|
282
|
+
ex:alice foaf:knows ex:bob .
|
|
283
|
+
ex:alice foaf:name "Alice" .
|
|
284
|
+
ex:bob foaf:name "Bob" .
|
|
285
|
+
}
|
|
286
|
+
WHERE {}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**Initial Graph**: 3 nodes (Alice, "Alice", Bob, "Bob"), 2 edges
|
|
290
|
+
|
|
291
|
+
**User Action**: Double-click the `ex:bob` node
|
|
292
|
+
|
|
293
|
+
**What Happens**:
|
|
294
|
+
- System executes: `DESCRIBE <http://example.org/bob>`
|
|
295
|
+
- Endpoint returns all triples about Bob (from your SPARQL endpoint)
|
|
296
|
+
- New nodes and edges appear in the graph
|
|
297
|
+
- Graph layout shifts smoothly to accommodate new nodes
|
|
298
|
+
- `ex:bob` node gets a thicker border
|
|
299
|
+
|
|
300
|
+
**Result**: You can now see Bob's relationships, properties, and connections without losing your current view
|
|
301
|
+
|
|
302
|
+
### Requirements
|
|
303
|
+
|
|
304
|
+
The node expansion feature requires:
|
|
305
|
+
|
|
306
|
+
1. **SPARQL 1.1 DESCRIBE support**: Your endpoint must support DESCRIBE queries
|
|
307
|
+
2. **Query execution callback**: YASR must provide `yasr.executeQuery()` for background queries
|
|
308
|
+
3. **RDF response format**: Endpoint must return results in RDF (JSON-LD, Turtle, N-Triples, etc.)
|
|
309
|
+
|
|
310
|
+
### Limitations & Behavior
|
|
311
|
+
|
|
312
|
+
- Only new triples are added (existing triples are skipped if already in graph)
|
|
313
|
+
- Expansion is **one-level deep** - only triples directly about the URI are added
|
|
314
|
+
- For very large result sets (1000+ triples from DESCRIBE), performance may be affected
|
|
315
|
+
- Blank nodes returned by DESCRIBE may not connect properly if disconnected from existing nodes
|
|
316
|
+
|
|
317
|
+
### Troubleshooting Expansion
|
|
318
|
+
|
|
319
|
+
**"Nothing happens when I double-click"**
|
|
320
|
+
- Ensure the node is blue (URI node, not literal or blank node)
|
|
321
|
+
- Check browser console for warnings about `yasr.executeQuery`
|
|
322
|
+
- Verify your SPARQL endpoint supports DESCRIBE queries
|
|
323
|
+
|
|
324
|
+
**"Graph becomes slow after many expansions"**
|
|
325
|
+
- Disable physics simulation in Settings panel for faster UI response
|
|
326
|
+
- Consider limiting query results with WHERE clause constraints
|
|
327
|
+
- Each expansion adds more triples to the visualization
|
|
328
|
+
|
|
329
|
+
**"New nodes don't appear where I expect"**
|
|
330
|
+
- The force-directed layout will position new nodes to minimize overlaps
|
|
331
|
+
- Disable Physics in Settings to lock positions if desired
|
|
332
|
+
- Manually drag new nodes to preferred positions
|
|
333
|
+
|
|
334
|
+
### Demo
|
|
335
|
+
|
|
336
|
+
See [demo/expand.html](./demo/expand.html) for a working example with mock DESCRIBE responses and detailed logging.
|
|
337
|
+
|
|
338
|
+
## �🔧 Development
|
|
244
339
|
|
|
245
340
|
### Build
|
|
246
341
|
|
|
@@ -272,6 +367,7 @@ npm run format # Prettier format
|
|
|
272
367
|
## 📚 Documentation
|
|
273
368
|
|
|
274
369
|
- **[Quickstart Guide](./specs/001-construct-graph-viz/quickstart.md)** - Installation, usage, troubleshooting
|
|
370
|
+
- **[Node Expansion Feature](./specs/001-construct-graph-viz/EXPAND_FEATURE.md)** - Complete guide to double-click expansion (FR-001 through FR-009)
|
|
275
371
|
- **[Data Model](./specs/001-construct-graph-viz/data-model.md)** - Entity definitions and relationships
|
|
276
372
|
- **[Contracts](./specs/001-construct-graph-viz/contracts/)** - API specifications for YASR plugin and vis-network integration
|
|
277
373
|
- **[Specification](./specs/001-construct-graph-viz/spec.md)** - Complete feature specification
|
|
@@ -312,19 +408,18 @@ Contributions welcome! Please follow the project constitution (`.specify/memory/
|
|
|
312
408
|
**Current Version**: 0.1.0 (MVP)
|
|
313
409
|
|
|
314
410
|
**Implemented Features** (v0.1.0):
|
|
315
|
-
- ✅ Basic graph visualization (US1)
|
|
316
|
-
- ✅ Navigation controls (US2)
|
|
317
|
-
- ✅ Color-coded nodes
|
|
318
|
-
- ✅ Prefix abbreviation
|
|
319
|
-
- ✅ Blank node support
|
|
320
|
-
- ✅
|
|
321
|
-
|
|
322
|
-
**
|
|
323
|
-
-
|
|
324
|
-
-
|
|
325
|
-
-
|
|
326
|
-
-
|
|
327
|
-
- ⏳ Layout algorithm selection
|
|
411
|
+
- ✅ **Basic graph visualization** (US1) - CONSTRUCT/DESCRIBE results as interactive graphs
|
|
412
|
+
- ✅ **Navigation controls** (US2) - Zoom, pan, "Fit to View" button
|
|
413
|
+
- ✅ **Color-coded nodes** - URIs, literals, blank nodes, rdf:type objects
|
|
414
|
+
- ✅ **Prefix abbreviation** - Display prefixed URIs instead of full URLs
|
|
415
|
+
- ✅ **Blank node support** - Handle anonymous RDF nodes
|
|
416
|
+
- ✅ **Drag & repositioning** - Manually adjust node positions
|
|
417
|
+
- ✅ **Rich tooltips** - Hover for detailed node/edge information
|
|
418
|
+
- ✅ **Theme support** - Light/dark mode detection and switching
|
|
419
|
+
- ✅ **Settings panel** - Configurable display options with localStorage persistence
|
|
420
|
+
- ✅ **Node icons & images** - Display images via schema:image property
|
|
421
|
+
- ✅ **Compact mode** - Hide literals and classes for cleaner visualization
|
|
422
|
+
- ✅ **Double-click expansion** (US5) - Fetch and merge related triples via DESCRIBE queries (see [Expand Nodes with Double Click](#-expand-nodes-with-double-click))
|
|
328
423
|
|
|
329
424
|
## 🐛 Troubleshooting
|
|
330
425
|
|
|
@@ -142,6 +142,40 @@ function getDefaultNetworkOptions(themeColors, settings) {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
// src/parsers.ts
|
|
145
|
+
async function parseBackgroundQueryResponse(response) {
|
|
146
|
+
var _a, _b;
|
|
147
|
+
if (!response) return [];
|
|
148
|
+
try {
|
|
149
|
+
let data2;
|
|
150
|
+
if (typeof response.json === "function") {
|
|
151
|
+
data2 = await response.json();
|
|
152
|
+
} else if (typeof response.data === "string") {
|
|
153
|
+
data2 = JSON.parse(response.data);
|
|
154
|
+
} else if (typeof response === "object") {
|
|
155
|
+
data2 = response;
|
|
156
|
+
} else {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
const bindings = (_b = (_a = data2 == null ? void 0 : data2.results) == null ? void 0 : _a.bindings) != null ? _b : [];
|
|
160
|
+
const triples = [];
|
|
161
|
+
for (const binding of bindings) {
|
|
162
|
+
if (!binding.subject || !binding.predicate || !binding.object || typeof binding.subject.value !== "string" || typeof binding.predicate.value !== "string" || typeof binding.object.value !== "string") continue;
|
|
163
|
+
triples.push({
|
|
164
|
+
subject: binding.subject.value,
|
|
165
|
+
predicate: binding.predicate.value,
|
|
166
|
+
object: {
|
|
167
|
+
value: binding.object.value,
|
|
168
|
+
type: binding.object.type || "uri",
|
|
169
|
+
datatype: binding.object.datatype,
|
|
170
|
+
lang: binding.object["xml:lang"]
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return triples;
|
|
175
|
+
} catch (e) {
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
}
|
|
145
179
|
function parseConstructResults(yasrResults) {
|
|
146
180
|
const triples = [];
|
|
147
181
|
if (!yasrResults || !yasrResults.getBindings) {
|
|
@@ -30014,10 +30048,16 @@ function saveSettings(settings) {
|
|
|
30014
30048
|
}
|
|
30015
30049
|
|
|
30016
30050
|
// src/GraphPlugin.ts
|
|
30051
|
+
var LOADING_BORDER_WIDTH = 4;
|
|
30052
|
+
var LOADING_BORDER_COLOR = "#ffa500";
|
|
30053
|
+
var EXPANDED_BORDER_WIDTH = 3;
|
|
30054
|
+
var DEFAULT_BORDER_WIDTH = 2;
|
|
30017
30055
|
var GraphPlugin = class {
|
|
30018
30056
|
constructor(yasr) {
|
|
30019
30057
|
this.settingsPanelOpen = false;
|
|
30020
30058
|
this.clickOutsideHandler = null;
|
|
30059
|
+
this.expansionAbortController = null;
|
|
30060
|
+
this.uriToNodeId = /* @__PURE__ */ new Map();
|
|
30021
30061
|
this.yasr = yasr;
|
|
30022
30062
|
this.network = null;
|
|
30023
30063
|
this.currentTheme = getCurrentTheme();
|
|
@@ -30041,6 +30081,12 @@ var GraphPlugin = class {
|
|
|
30041
30081
|
static get label() {
|
|
30042
30082
|
return "Graph";
|
|
30043
30083
|
}
|
|
30084
|
+
/**
|
|
30085
|
+
* Help/documentation URL
|
|
30086
|
+
*/
|
|
30087
|
+
static get helpReference() {
|
|
30088
|
+
return "https://yasgui-doc.matdata.eu/docs/user-guide#graph-plugin";
|
|
30089
|
+
}
|
|
30044
30090
|
/**
|
|
30045
30091
|
* Check if plugin can handle the current results
|
|
30046
30092
|
* @returns True if results are from CONSTRUCT or DESCRIBE query
|
|
@@ -30062,6 +30108,12 @@ var GraphPlugin = class {
|
|
|
30062
30108
|
*/
|
|
30063
30109
|
draw() {
|
|
30064
30110
|
const wasPanelOpen = this.settingsPanelOpen;
|
|
30111
|
+
if (this.expansionAbortController) {
|
|
30112
|
+
this.expansionAbortController.abort();
|
|
30113
|
+
this.expansionAbortController = null;
|
|
30114
|
+
}
|
|
30115
|
+
this.expandedNodes = /* @__PURE__ */ new Set();
|
|
30116
|
+
this.uriToNodeId = /* @__PURE__ */ new Map();
|
|
30065
30117
|
this.yasr.resultsEl.innerHTML = "";
|
|
30066
30118
|
try {
|
|
30067
30119
|
this.triples = parseConstructResults(this.yasr.results);
|
|
@@ -30118,6 +30170,13 @@ var GraphPlugin = class {
|
|
|
30118
30170
|
this.nodesDataSet.update(updates);
|
|
30119
30171
|
}
|
|
30120
30172
|
});
|
|
30173
|
+
this.setupNodeExpansion();
|
|
30174
|
+
this.uriToNodeId = /* @__PURE__ */ new Map();
|
|
30175
|
+
this.nodesDataSet.get().forEach((node) => {
|
|
30176
|
+
if (node.uri) {
|
|
30177
|
+
this.uriToNodeId.set(node.uri, node.id);
|
|
30178
|
+
}
|
|
30179
|
+
});
|
|
30121
30180
|
if (!this.themeObserver) {
|
|
30122
30181
|
this.themeObserver = watchThemeChanges((newTheme) => {
|
|
30123
30182
|
this.applyTheme(newTheme);
|
|
@@ -30500,7 +30559,119 @@ var GraphPlugin = class {
|
|
|
30500
30559
|
return panel;
|
|
30501
30560
|
}
|
|
30502
30561
|
/**
|
|
30503
|
-
*
|
|
30562
|
+
* Setup double-click handler for node expansion
|
|
30563
|
+
*/
|
|
30564
|
+
setupNodeExpansion() {
|
|
30565
|
+
if (!this.network) return;
|
|
30566
|
+
this.network.on("doubleClick", (params) => {
|
|
30567
|
+
if (params.nodes.length === 0) return;
|
|
30568
|
+
const nodeId = params.nodes[0];
|
|
30569
|
+
const node = this.nodesDataSet.get(nodeId);
|
|
30570
|
+
if (node && node.uri && !node.uri.startsWith("_:")) {
|
|
30571
|
+
this.expandNode(node.uri);
|
|
30572
|
+
}
|
|
30573
|
+
});
|
|
30574
|
+
}
|
|
30575
|
+
/**
|
|
30576
|
+
* Expand a node by executing a DESCRIBE query for the given URI and
|
|
30577
|
+
* merging the returned triples into the existing graph.
|
|
30578
|
+
* @param uri - URI of the node to expand
|
|
30579
|
+
*/
|
|
30580
|
+
async expandNode(uri) {
|
|
30581
|
+
if (!this.yasr.executeQuery) {
|
|
30582
|
+
console.warn("yasgui-graph-plugin: background query execution not available (yasr.executeQuery is not configured)");
|
|
30583
|
+
return;
|
|
30584
|
+
}
|
|
30585
|
+
if (!this.triples || !this.prefixMap) return;
|
|
30586
|
+
if (this.expansionAbortController) {
|
|
30587
|
+
this.expansionAbortController.abort();
|
|
30588
|
+
}
|
|
30589
|
+
const controller = new AbortController();
|
|
30590
|
+
this.expansionAbortController = controller;
|
|
30591
|
+
const nodeId = this.uriToNodeId.get(uri);
|
|
30592
|
+
let originalColor = void 0;
|
|
30593
|
+
let originalBorderWidth = void 0;
|
|
30594
|
+
if (nodeId !== void 0) {
|
|
30595
|
+
const node = this.nodesDataSet.get(nodeId);
|
|
30596
|
+
if (node) {
|
|
30597
|
+
originalColor = node.color;
|
|
30598
|
+
originalBorderWidth = node.borderWidth;
|
|
30599
|
+
}
|
|
30600
|
+
this.nodesDataSet.update({
|
|
30601
|
+
id: nodeId,
|
|
30602
|
+
borderWidth: LOADING_BORDER_WIDTH,
|
|
30603
|
+
color: typeof originalColor === "object" && originalColor !== null ? { ...originalColor, border: LOADING_BORDER_COLOR } : { border: LOADING_BORDER_COLOR, background: originalColor != null ? originalColor : void 0 }
|
|
30604
|
+
});
|
|
30605
|
+
}
|
|
30606
|
+
const restoreNode = (borderWidth) => {
|
|
30607
|
+
if (nodeId !== void 0) {
|
|
30608
|
+
this.nodesDataSet.update({ id: nodeId, borderWidth, color: originalColor });
|
|
30609
|
+
}
|
|
30610
|
+
};
|
|
30611
|
+
try {
|
|
30612
|
+
const response = await this.yasr.executeQuery(`DESCRIBE <${uri}>`, {
|
|
30613
|
+
acceptHeader: "application/sparql-results+json",
|
|
30614
|
+
signal: controller.signal
|
|
30615
|
+
});
|
|
30616
|
+
if (controller.signal.aborted) {
|
|
30617
|
+
restoreNode(originalBorderWidth != null ? originalBorderWidth : DEFAULT_BORDER_WIDTH);
|
|
30618
|
+
return;
|
|
30619
|
+
}
|
|
30620
|
+
const newTriples = await parseBackgroundQueryResponse(response);
|
|
30621
|
+
if (controller.signal.aborted) {
|
|
30622
|
+
restoreNode(originalBorderWidth != null ? originalBorderWidth : DEFAULT_BORDER_WIDTH);
|
|
30623
|
+
return;
|
|
30624
|
+
}
|
|
30625
|
+
const existingKeys = new Set(
|
|
30626
|
+
this.triples.map((t) => `${t.subject}|${t.predicate}|${t.object.value}`)
|
|
30627
|
+
);
|
|
30628
|
+
const uniqueNew = newTriples.filter(
|
|
30629
|
+
(t) => !existingKeys.has(`${t.subject}|${t.predicate}|${t.object.value}`)
|
|
30630
|
+
);
|
|
30631
|
+
if (uniqueNew.length > 0) {
|
|
30632
|
+
this.triples = [...this.triples, ...uniqueNew];
|
|
30633
|
+
this.mergeNewTriples();
|
|
30634
|
+
}
|
|
30635
|
+
restoreNode(EXPANDED_BORDER_WIDTH);
|
|
30636
|
+
} catch (error) {
|
|
30637
|
+
if ((error == null ? void 0 : error.name) === "AbortError") {
|
|
30638
|
+
restoreNode(originalBorderWidth != null ? originalBorderWidth : DEFAULT_BORDER_WIDTH);
|
|
30639
|
+
return;
|
|
30640
|
+
}
|
|
30641
|
+
console.error("yasgui-graph-plugin: error expanding node", uri, error);
|
|
30642
|
+
restoreNode(originalBorderWidth != null ? originalBorderWidth : DEFAULT_BORDER_WIDTH);
|
|
30643
|
+
}
|
|
30644
|
+
}
|
|
30645
|
+
/**
|
|
30646
|
+
* Incrementally add new triples to the vis-network DataSets without a full redraw.
|
|
30647
|
+
* New nodes and edges are added; existing ones are left untouched.
|
|
30648
|
+
* Expects `this.triples` to already include the new triples.
|
|
30649
|
+
*/
|
|
30650
|
+
mergeNewTriples() {
|
|
30651
|
+
if (!this.triples || !this.prefixMap || !this.nodesDataSet || !this.edgesDataSet) return;
|
|
30652
|
+
const themeColors = getThemeNodeColors(this.currentTheme);
|
|
30653
|
+
const { nodes, edges } = triplesToGraph(this.triples, this.prefixMap, themeColors, this.settings);
|
|
30654
|
+
const existingValues = new Set(this.nodesDataSet.get().map((n) => n.fullValue));
|
|
30655
|
+
const nodesToAdd = nodes.filter((n) => !existingValues.has(n.fullValue));
|
|
30656
|
+
const existingEdgeKeys = new Set(
|
|
30657
|
+
this.edgesDataSet.get().map((e) => `${e.from}|${e.predicate}|${e.to}`)
|
|
30658
|
+
);
|
|
30659
|
+
const edgesToAdd = edges.filter(
|
|
30660
|
+
(e) => !existingEdgeKeys.has(`${e.from}|${e.predicate}|${e.to}`)
|
|
30661
|
+
);
|
|
30662
|
+
if (nodesToAdd.length > 0) {
|
|
30663
|
+
this.nodesDataSet.add(nodesToAdd);
|
|
30664
|
+
nodesToAdd.forEach((n) => {
|
|
30665
|
+
if (n.uri != null) {
|
|
30666
|
+
this.uriToNodeId.set(n.uri, n.id);
|
|
30667
|
+
}
|
|
30668
|
+
});
|
|
30669
|
+
}
|
|
30670
|
+
if (edgesToAdd.length > 0) {
|
|
30671
|
+
this.edgesDataSet.add(edgesToAdd);
|
|
30672
|
+
}
|
|
30673
|
+
}
|
|
30674
|
+
/**
|
|
30504
30675
|
* @returns Icon element
|
|
30505
30676
|
*/
|
|
30506
30677
|
getIcon() {
|
|
@@ -30522,6 +30693,10 @@ var GraphPlugin = class {
|
|
|
30522
30693
|
*/
|
|
30523
30694
|
destroy() {
|
|
30524
30695
|
this.removeClickOutsideHandler();
|
|
30696
|
+
if (this.expansionAbortController) {
|
|
30697
|
+
this.expansionAbortController.abort();
|
|
30698
|
+
this.expansionAbortController = null;
|
|
30699
|
+
}
|
|
30525
30700
|
if (this.themeObserver) {
|
|
30526
30701
|
this.themeObserver.disconnect();
|
|
30527
30702
|
this.themeObserver = null;
|