@matdata/yasgui-graph-plugin 1.4.2 → 1.5.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 +118 -16
- package/dist/yasgui-graph-plugin.cjs.css +115 -0
- package/dist/yasgui-graph-plugin.cjs.css.map +2 -2
- package/dist/yasgui-graph-plugin.cjs.js +641 -27
- package/dist/yasgui-graph-plugin.cjs.js.map +4 -4
- package/dist/yasgui-graph-plugin.css +114 -1
- package/dist/yasgui-graph-plugin.esm.css +115 -0
- package/dist/yasgui-graph-plugin.esm.css.map +2 -2
- package/dist/yasgui-graph-plugin.esm.js +641 -27
- package/dist/yasgui-graph-plugin.esm.js.map +4 -4
- package/dist/yasgui-graph-plugin.min.css +1 -1
- package/dist/yasgui-graph-plugin.min.css.map +2 -2
- package/dist/yasgui-graph-plugin.min.js +24 -20
- package/dist/yasgui-graph-plugin.min.js.map +4 -4
- package/package.json +2 -2
|
@@ -67,14 +67,18 @@ function truncateLabel(text, maxLength = 50) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
// src/networkConfig.ts
|
|
70
|
-
function getDefaultNetworkOptions(themeColors) {
|
|
70
|
+
function getDefaultNetworkOptions(themeColors, settings) {
|
|
71
|
+
const curved = !settings || settings.edgeStyle !== "straight";
|
|
72
|
+
const nodeSizeMap = { small: 6, medium: 10, large: 16 };
|
|
73
|
+
const nodeSize = (settings == null ? void 0 : settings.nodeSize) ? nodeSizeMap[settings.nodeSize] : 10;
|
|
74
|
+
const showNodeLabels = (settings == null ? void 0 : settings.showNodeLabels) !== false;
|
|
71
75
|
return {
|
|
72
76
|
// Configure canvas background color based on theme
|
|
73
77
|
configure: {
|
|
74
78
|
enabled: false
|
|
75
79
|
},
|
|
76
80
|
physics: {
|
|
77
|
-
enabled:
|
|
81
|
+
enabled: (settings == null ? void 0 : settings.physicsEnabled) !== false,
|
|
78
82
|
stabilization: {
|
|
79
83
|
enabled: true,
|
|
80
84
|
iterations: 200,
|
|
@@ -94,14 +98,16 @@ function getDefaultNetworkOptions(themeColors) {
|
|
|
94
98
|
dragView: true,
|
|
95
99
|
zoomView: true,
|
|
96
100
|
hover: true,
|
|
97
|
-
tooltipDelay: 300
|
|
101
|
+
tooltipDelay: 300,
|
|
98
102
|
// 300ms hover delay per spec
|
|
103
|
+
hideEdgesOnDrag: false,
|
|
104
|
+
hideEdgesOnZoom: false
|
|
99
105
|
},
|
|
100
106
|
nodes: {
|
|
101
107
|
shape: "dot",
|
|
102
|
-
size:
|
|
108
|
+
size: nodeSize,
|
|
103
109
|
font: {
|
|
104
|
-
size: 12,
|
|
110
|
+
size: showNodeLabels ? 12 : 0,
|
|
105
111
|
color: themeColors.text
|
|
106
112
|
},
|
|
107
113
|
borderWidth: 1,
|
|
@@ -116,7 +122,7 @@ function getDefaultNetworkOptions(themeColors) {
|
|
|
116
122
|
}
|
|
117
123
|
},
|
|
118
124
|
smooth: {
|
|
119
|
-
enabled:
|
|
125
|
+
enabled: curved,
|
|
120
126
|
type: "dynamic",
|
|
121
127
|
roundness: 0.5
|
|
122
128
|
},
|
|
@@ -178,10 +184,129 @@ function getNodeColor(node, triples, themeColors) {
|
|
|
178
184
|
return themeColors.uri;
|
|
179
185
|
}
|
|
180
186
|
|
|
187
|
+
// src/predicateIcons.ts
|
|
188
|
+
var PREDICATE_ICONS = {
|
|
189
|
+
// RDF core
|
|
190
|
+
"http://www.w3.org/1999/02/22-rdf-syntax-ns#type": "a",
|
|
191
|
+
// Turtle's "a" shorthand
|
|
192
|
+
"http://www.w3.org/1999/02/22-rdf-syntax-ns#value": "val",
|
|
193
|
+
// RDFS
|
|
194
|
+
"http://www.w3.org/2000/01/rdf-schema#label": "lbl",
|
|
195
|
+
"http://www.w3.org/2000/01/rdf-schema#comment": "cmt",
|
|
196
|
+
"http://www.w3.org/2000/01/rdf-schema#subClassOf": "\u2282",
|
|
197
|
+
// ⊂
|
|
198
|
+
"http://www.w3.org/2000/01/rdf-schema#subPropertyOf": "\u2286",
|
|
199
|
+
// ⊆
|
|
200
|
+
"http://www.w3.org/2000/01/rdf-schema#domain": "dom",
|
|
201
|
+
"http://www.w3.org/2000/01/rdf-schema#range": "rng",
|
|
202
|
+
"http://www.w3.org/2000/01/rdf-schema#seeAlso": "see",
|
|
203
|
+
"http://www.w3.org/2000/01/rdf-schema#isDefinedBy": "idb",
|
|
204
|
+
// OWL
|
|
205
|
+
"http://www.w3.org/2002/07/owl#sameAs": "\u2261",
|
|
206
|
+
// ≡
|
|
207
|
+
"http://www.w3.org/2002/07/owl#equivalentClass": "\u2245",
|
|
208
|
+
// ≅
|
|
209
|
+
"http://www.w3.org/2002/07/owl#inverseOf": "\u21C4",
|
|
210
|
+
// ⇄
|
|
211
|
+
"http://www.w3.org/2002/07/owl#disjointWith": "\u2260",
|
|
212
|
+
// ≠
|
|
213
|
+
// SKOS
|
|
214
|
+
"http://www.w3.org/2004/02/skos/core#prefLabel": "\u2605",
|
|
215
|
+
// ★
|
|
216
|
+
"http://www.w3.org/2004/02/skos/core#altLabel": "\u2606",
|
|
217
|
+
// ☆
|
|
218
|
+
"http://www.w3.org/2004/02/skos/core#definition": "def",
|
|
219
|
+
"http://www.w3.org/2004/02/skos/core#broader": "\u2191",
|
|
220
|
+
// ↑
|
|
221
|
+
"http://www.w3.org/2004/02/skos/core#narrower": "\u2193",
|
|
222
|
+
// ↓
|
|
223
|
+
"http://www.w3.org/2004/02/skos/core#related": "\u2194",
|
|
224
|
+
// ↔
|
|
225
|
+
"http://www.w3.org/2004/02/skos/core#note": "note",
|
|
226
|
+
"http://www.w3.org/2004/02/skos/core#exactMatch": "\u2261",
|
|
227
|
+
// ≡
|
|
228
|
+
"http://www.w3.org/2004/02/skos/core#closeMatch": "\u2248",
|
|
229
|
+
// ≈
|
|
230
|
+
// Dublin Core Terms
|
|
231
|
+
"http://purl.org/dc/terms/title": "ttl",
|
|
232
|
+
"http://purl.org/dc/terms/description": "dsc",
|
|
233
|
+
"http://purl.org/dc/terms/created": "crt",
|
|
234
|
+
"http://purl.org/dc/terms/modified": "mod",
|
|
235
|
+
"http://purl.org/dc/terms/creator": "by",
|
|
236
|
+
"http://purl.org/dc/terms/subject": "sbj",
|
|
237
|
+
// FOAF
|
|
238
|
+
"http://xmlns.com/foaf/0.1/name": "nm",
|
|
239
|
+
"http://xmlns.com/foaf/0.1/knows": "\u27F7",
|
|
240
|
+
// ⟷
|
|
241
|
+
"http://xmlns.com/foaf/0.1/member": "mbr",
|
|
242
|
+
// Schema.org
|
|
243
|
+
"http://schema.org/name": "nm",
|
|
244
|
+
"http://schema.org/description": "dsc"
|
|
245
|
+
};
|
|
246
|
+
function getPredicateIcon(predicateUri) {
|
|
247
|
+
return PREDICATE_ICONS[predicateUri];
|
|
248
|
+
}
|
|
249
|
+
|
|
181
250
|
// src/transformers.ts
|
|
182
|
-
|
|
251
|
+
var RDF_TYPE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";
|
|
252
|
+
var SCHEMA_IMAGE = "https://schema.org/image";
|
|
253
|
+
var SCHEMA_ICON = "https://schema.org/icon";
|
|
254
|
+
var RDFS_LABEL = "http://www.w3.org/2000/01/rdf-schema#label";
|
|
255
|
+
var RDFS_SUBCLASSOF = "http://www.w3.org/2000/01/rdf-schema#subClassOf";
|
|
256
|
+
var SUPPRESSED_PREDICATES = /* @__PURE__ */ new Set([SCHEMA_IMAGE, SCHEMA_ICON, RDFS_LABEL]);
|
|
257
|
+
function appendTooltipRows(title, rows) {
|
|
258
|
+
const closingTag = "</div>";
|
|
259
|
+
const idx = title.lastIndexOf(closingTag);
|
|
260
|
+
if (idx === -1) return title + rows;
|
|
261
|
+
return title.slice(0, idx) + rows + closingTag;
|
|
262
|
+
}
|
|
263
|
+
function buildVisualTooltipRow(key, value) {
|
|
264
|
+
return `<div class="yasgui-tooltip-row"><span class="yasgui-tooltip-key">${escapeHtml(key)}</span><span class="yasgui-tooltip-val">${escapeHtml(value)}</span></div>`;
|
|
265
|
+
}
|
|
266
|
+
function escapeHtml(str) {
|
|
267
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
268
|
+
}
|
|
269
|
+
function createCompactNodeTooltipHTML(uri, triples, prefixMap) {
|
|
270
|
+
const isBlankNode = uri.startsWith("_:");
|
|
271
|
+
let rows = `<div class="yasgui-tooltip-type">${isBlankNode ? "Blank Node" : "URI"}</div>`;
|
|
272
|
+
rows += `<div class="yasgui-tooltip-row"><span class="yasgui-tooltip-key">${isBlankNode ? "Identifier" : "Full URI"}</span><span class="yasgui-tooltip-val">${escapeHtml(uri)}</span></div>`;
|
|
273
|
+
triples.filter((t) => t.subject === uri && t.predicate === RDF_TYPE).forEach((t) => {
|
|
274
|
+
const typeLabel = applyPrefix(t.object.value, prefixMap);
|
|
275
|
+
rows += `<div class="yasgui-tooltip-row"><span class="yasgui-tooltip-key">rdf:type</span><span class="yasgui-tooltip-val">${escapeHtml(typeLabel)}</span></div>`;
|
|
276
|
+
});
|
|
277
|
+
triples.filter((t) => t.subject === uri && t.object.type === "literal").forEach((t) => {
|
|
278
|
+
const predLabel = applyPrefix(t.predicate, prefixMap);
|
|
279
|
+
rows += `<div class="yasgui-tooltip-row"><span class="yasgui-tooltip-key">${escapeHtml(predLabel)}</span><span class="yasgui-tooltip-val">${escapeHtml(t.object.value)}</span></div>`;
|
|
280
|
+
});
|
|
281
|
+
return `<div class="yasgui-graph-tooltip">${rows}</div>`;
|
|
282
|
+
}
|
|
283
|
+
function createNodeTooltipHTML(nodeType, value, datatype, lang, prefixMap) {
|
|
284
|
+
const typeLabel = nodeType === "uri" ? "URI" : nodeType === "literal" ? "Literal" : "Blank Node";
|
|
285
|
+
let rows = `<div class="yasgui-tooltip-type">${typeLabel}</div>`;
|
|
286
|
+
if (nodeType === "literal") {
|
|
287
|
+
rows += `<div class="yasgui-tooltip-row"><span class="yasgui-tooltip-key">Value</span><span class="yasgui-tooltip-val">${escapeHtml(value)}</span></div>`;
|
|
288
|
+
if (datatype) {
|
|
289
|
+
const dtLabel = prefixMap ? applyPrefix(datatype, prefixMap) : datatype;
|
|
290
|
+
rows += `<div class="yasgui-tooltip-row"><span class="yasgui-tooltip-key">Datatype</span><span class="yasgui-tooltip-val">${escapeHtml(dtLabel)}</span></div>`;
|
|
291
|
+
}
|
|
292
|
+
if (lang) {
|
|
293
|
+
rows += `<div class="yasgui-tooltip-row"><span class="yasgui-tooltip-key">Language</span><span class="yasgui-tooltip-val">${escapeHtml(lang)}</span></div>`;
|
|
294
|
+
}
|
|
295
|
+
} else if (nodeType === "uri") {
|
|
296
|
+
rows += `<div class="yasgui-tooltip-row"><span class="yasgui-tooltip-key">Full URI</span><span class="yasgui-tooltip-val">${escapeHtml(value)}</span></div>`;
|
|
297
|
+
} else if (nodeType === "bnode") {
|
|
298
|
+
rows += `<div class="yasgui-tooltip-row"><span class="yasgui-tooltip-key">Identifier</span><span class="yasgui-tooltip-val">${escapeHtml(value)}</span></div>`;
|
|
299
|
+
}
|
|
300
|
+
return `<div class="yasgui-graph-tooltip">${rows}</div>`;
|
|
301
|
+
}
|
|
302
|
+
function createEdgeTooltipHTML(predicateUri) {
|
|
303
|
+
const rows = `<div class="yasgui-tooltip-type">Predicate</div><div class="yasgui-tooltip-row"><span class="yasgui-tooltip-key">Full URI</span><span class="yasgui-tooltip-val">${escapeHtml(predicateUri)}</span></div>`;
|
|
304
|
+
return `<div class="yasgui-graph-tooltip">${rows}</div>`;
|
|
305
|
+
}
|
|
306
|
+
function createNodeMap(triples, prefixMap, themeColors, settings) {
|
|
183
307
|
const nodeMap = /* @__PURE__ */ new Map();
|
|
184
308
|
let nodeId = 1;
|
|
309
|
+
const sizeMultiplier = (settings == null ? void 0 : settings.nodeSize) === "small" ? 0.5 : (settings == null ? void 0 : settings.nodeSize) === "large" ? 2 : 1;
|
|
185
310
|
triples.forEach((triple) => {
|
|
186
311
|
if (!nodeMap.has(triple.subject)) {
|
|
187
312
|
const isBlankNode = triple.subject.startsWith("_:");
|
|
@@ -193,11 +318,18 @@ function createNodeMap(triples, prefixMap, themeColors) {
|
|
|
193
318
|
color: getNodeColor({ uri: triple.subject, type: "uri" }, triples, themeColors),
|
|
194
319
|
type: "uri",
|
|
195
320
|
fullValue: triple.subject,
|
|
196
|
-
|
|
321
|
+
size: 10 * sizeMultiplier,
|
|
322
|
+
title: createNodeTooltipHTML(
|
|
323
|
+
isBlankNode ? "bnode" : "uri",
|
|
324
|
+
triple.subject,
|
|
325
|
+
void 0,
|
|
326
|
+
void 0,
|
|
327
|
+
prefixMap
|
|
328
|
+
)
|
|
197
329
|
});
|
|
198
330
|
}
|
|
199
331
|
const objValue = triple.object.value;
|
|
200
|
-
if (!nodeMap.has(objValue)) {
|
|
332
|
+
if (!nodeMap.has(objValue) && !SUPPRESSED_PREDICATES.has(triple.predicate)) {
|
|
201
333
|
const isLiteral = triple.object.type === "literal";
|
|
202
334
|
const isBlankNode = !isLiteral && objValue.startsWith("_:");
|
|
203
335
|
let label;
|
|
@@ -206,15 +338,21 @@ function createNodeMap(triples, prefixMap, themeColors) {
|
|
|
206
338
|
if (isLiteral) {
|
|
207
339
|
label = truncateLabel(objValue);
|
|
208
340
|
fullValue = objValue;
|
|
209
|
-
title =
|
|
341
|
+
title = createNodeTooltipHTML(
|
|
342
|
+
"literal",
|
|
343
|
+
objValue,
|
|
344
|
+
triple.object.datatype,
|
|
345
|
+
triple.object.lang,
|
|
346
|
+
prefixMap
|
|
347
|
+
);
|
|
210
348
|
} else if (isBlankNode) {
|
|
211
349
|
label = objValue;
|
|
212
350
|
fullValue = objValue;
|
|
213
|
-
title = objValue;
|
|
351
|
+
title = createNodeTooltipHTML("bnode", objValue);
|
|
214
352
|
} else {
|
|
215
353
|
label = truncateLabel(applyPrefix(objValue, prefixMap));
|
|
216
354
|
fullValue = objValue;
|
|
217
|
-
title =
|
|
355
|
+
title = createNodeTooltipHTML("uri", objValue, void 0, void 0, prefixMap);
|
|
218
356
|
}
|
|
219
357
|
nodeMap.set(objValue, {
|
|
220
358
|
id: nodeId++,
|
|
@@ -227,39 +365,157 @@ function createNodeMap(triples, prefixMap, themeColors) {
|
|
|
227
365
|
),
|
|
228
366
|
type: isLiteral ? "literal" : "uri",
|
|
229
367
|
fullValue,
|
|
368
|
+
size: (isLiteral ? 5 : 10) * sizeMultiplier,
|
|
230
369
|
title
|
|
231
370
|
});
|
|
232
371
|
}
|
|
233
372
|
});
|
|
234
373
|
return nodeMap;
|
|
235
374
|
}
|
|
236
|
-
function createEdgesArray(triples, nodeMap, prefixMap) {
|
|
375
|
+
function createEdgesArray(triples, nodeMap, prefixMap, settings) {
|
|
237
376
|
const edges = [];
|
|
238
377
|
const edgeSet = /* @__PURE__ */ new Set();
|
|
239
378
|
triples.forEach((triple) => {
|
|
379
|
+
var _a, _b;
|
|
380
|
+
if (SUPPRESSED_PREDICATES.has(triple.predicate)) return;
|
|
240
381
|
const fromNode = nodeMap.get(triple.subject);
|
|
241
382
|
const toNode = nodeMap.get(triple.object.value);
|
|
242
383
|
if (!fromNode || !toNode) return;
|
|
243
384
|
const edgeKey = `${fromNode.id}-${triple.predicate}-${toNode.id}`;
|
|
244
385
|
if (!edgeSet.has(edgeKey)) {
|
|
245
386
|
edgeSet.add(edgeKey);
|
|
387
|
+
let edgeLabel;
|
|
388
|
+
const predicateDisplay = (_a = settings == null ? void 0 : settings.predicateDisplay) != null ? _a : "label";
|
|
389
|
+
if (predicateDisplay === "none") {
|
|
390
|
+
edgeLabel = "";
|
|
391
|
+
} else if (predicateDisplay === "icon") {
|
|
392
|
+
edgeLabel = (_b = getPredicateIcon(triple.predicate)) != null ? _b : truncateLabel(applyPrefix(triple.predicate, prefixMap));
|
|
393
|
+
} else {
|
|
394
|
+
edgeLabel = truncateLabel(applyPrefix(triple.predicate, prefixMap));
|
|
395
|
+
}
|
|
246
396
|
edges.push({
|
|
247
397
|
id: `edge_${fromNode.id}_${toNode.id}_${edges.length}`,
|
|
248
398
|
from: fromNode.id,
|
|
249
399
|
to: toNode.id,
|
|
250
|
-
label:
|
|
400
|
+
label: edgeLabel,
|
|
251
401
|
predicate: triple.predicate,
|
|
252
|
-
title:
|
|
402
|
+
title: createEdgeTooltipHTML(triple.predicate),
|
|
253
403
|
arrows: "to"
|
|
254
404
|
});
|
|
255
405
|
}
|
|
256
406
|
});
|
|
257
407
|
return edges;
|
|
258
408
|
}
|
|
259
|
-
function
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
409
|
+
function isNodeVisible(node, triples, settings) {
|
|
410
|
+
if (node.uri && node.uri.startsWith("_:")) {
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
413
|
+
if (!settings.compactMode) {
|
|
414
|
+
return true;
|
|
415
|
+
}
|
|
416
|
+
if (node.type === "literal") {
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
const isClass = triples.some(
|
|
420
|
+
(t) => t.predicate === "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" && t.object.value === node.uri
|
|
421
|
+
);
|
|
422
|
+
if (isClass) {
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
function triplesToGraph(triples, prefixMap, themeColors, settings) {
|
|
428
|
+
const nodeMap = createNodeMap(triples, prefixMap, themeColors, settings);
|
|
429
|
+
const sizeMultiplier = (settings == null ? void 0 : settings.nodeSize) === "small" ? 0.5 : (settings == null ? void 0 : settings.nodeSize) === "large" ? 2 : 1;
|
|
430
|
+
const tripleIndex = /* @__PURE__ */ new Map();
|
|
431
|
+
for (const t of triples) {
|
|
432
|
+
if (!tripleIndex.has(t.subject)) tripleIndex.set(t.subject, /* @__PURE__ */ new Map());
|
|
433
|
+
const sp = tripleIndex.get(t.subject);
|
|
434
|
+
if (!sp.has(t.predicate)) sp.set(t.predicate, []);
|
|
435
|
+
sp.get(t.predicate).push(t.object.value);
|
|
436
|
+
}
|
|
437
|
+
function getNodeVisualIdx(uri) {
|
|
438
|
+
const sp = tripleIndex.get(uri);
|
|
439
|
+
if (!sp) return {};
|
|
440
|
+
const icons = sp.get(SCHEMA_ICON);
|
|
441
|
+
if (icons == null ? void 0 : icons.length) return { icon: icons[0] };
|
|
442
|
+
const images = sp.get(SCHEMA_IMAGE);
|
|
443
|
+
if (images == null ? void 0 : images.length) return { image: images[0] };
|
|
444
|
+
return {};
|
|
445
|
+
}
|
|
446
|
+
function resolveCompactVisualIdx(uri) {
|
|
447
|
+
var _a, _b, _c, _d;
|
|
448
|
+
const own = getNodeVisualIdx(uri);
|
|
449
|
+
if (own.icon || own.image) return own;
|
|
450
|
+
const typeUris = (_b = (_a = tripleIndex.get(uri)) == null ? void 0 : _a.get(RDF_TYPE)) != null ? _b : [];
|
|
451
|
+
for (const typeUri of typeUris) {
|
|
452
|
+
const cv = getNodeVisualIdx(typeUri);
|
|
453
|
+
if (cv.icon || cv.image) return cv;
|
|
454
|
+
}
|
|
455
|
+
for (const typeUri of typeUris) {
|
|
456
|
+
const superUris = (_d = (_c = tripleIndex.get(typeUri)) == null ? void 0 : _c.get(RDFS_SUBCLASSOF)) != null ? _d : [];
|
|
457
|
+
for (const superUri of superUris) {
|
|
458
|
+
const sv = getNodeVisualIdx(superUri);
|
|
459
|
+
if (sv.icon || sv.image) return sv;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return {};
|
|
463
|
+
}
|
|
464
|
+
if (settings == null ? void 0 : settings.compactMode) {
|
|
465
|
+
const subjects = new Set(triples.map((t) => t.subject));
|
|
466
|
+
subjects.forEach((subjectUri) => {
|
|
467
|
+
const node = nodeMap.get(subjectUri);
|
|
468
|
+
if (node) {
|
|
469
|
+
node.title = createCompactNodeTooltipHTML(subjectUri, triples, prefixMap);
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
nodeMap.forEach((node) => {
|
|
474
|
+
var _a, _b;
|
|
475
|
+
if (!node.uri || node.type === "literal") return;
|
|
476
|
+
const visual = (settings == null ? void 0 : settings.compactMode) ? resolveCompactVisualIdx(node.uri) : getNodeVisualIdx(node.uri);
|
|
477
|
+
const rdfsLabel = (_b = (_a = tripleIndex.get(node.uri)) == null ? void 0 : _a.get(RDFS_LABEL)) == null ? void 0 : _b[0];
|
|
478
|
+
if (visual.icon) {
|
|
479
|
+
node.shape = "text";
|
|
480
|
+
node.label = rdfsLabel ? `${visual.icon}
|
|
481
|
+
${rdfsLabel}` : visual.icon;
|
|
482
|
+
node.font = { size: 14 * sizeMultiplier };
|
|
483
|
+
if (!(settings == null ? void 0 : settings.compactMode)) {
|
|
484
|
+
node.title = appendTooltipRows(node.title, buildVisualTooltipRow("Icon", visual.icon));
|
|
485
|
+
}
|
|
486
|
+
} else if (visual.image) {
|
|
487
|
+
let imageAllowed = false;
|
|
488
|
+
try {
|
|
489
|
+
const parsed = new URL(visual.image);
|
|
490
|
+
imageAllowed = parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
491
|
+
} catch (e) {
|
|
492
|
+
}
|
|
493
|
+
if (imageAllowed) {
|
|
494
|
+
node.shape = "circularImage";
|
|
495
|
+
node.image = visual.image;
|
|
496
|
+
}
|
|
497
|
+
if (rdfsLabel) {
|
|
498
|
+
node.label = rdfsLabel;
|
|
499
|
+
node.font = { size: 14 * sizeMultiplier };
|
|
500
|
+
}
|
|
501
|
+
if (!(settings == null ? void 0 : settings.compactMode)) {
|
|
502
|
+
node.title = appendTooltipRows(node.title, buildVisualTooltipRow("Image", visual.image));
|
|
503
|
+
}
|
|
504
|
+
} else if (rdfsLabel) {
|
|
505
|
+
node.label = rdfsLabel;
|
|
506
|
+
}
|
|
507
|
+
if (rdfsLabel && !(settings == null ? void 0 : settings.compactMode)) {
|
|
508
|
+
node.title = appendTooltipRows(node.title, buildVisualTooltipRow("rdfs:label", rdfsLabel));
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
const visibleNodeIds = /* @__PURE__ */ new Set();
|
|
512
|
+
nodeMap.forEach((node) => {
|
|
513
|
+
if (!settings || isNodeVisible(node, triples, settings)) {
|
|
514
|
+
visibleNodeIds.add(node.id);
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
const edges = createEdgesArray(triples, nodeMap, prefixMap, settings).filter((e) => visibleNodeIds.has(e.from) && visibleNodeIds.has(e.to));
|
|
518
|
+
const nodes = Array.from(nodeMap.values()).filter((n) => visibleNodeIds.has(n.id));
|
|
263
519
|
return { nodes, edges };
|
|
264
520
|
}
|
|
265
521
|
|
|
@@ -29730,9 +29986,38 @@ function watchThemeChanges(callback) {
|
|
|
29730
29986
|
return observer;
|
|
29731
29987
|
}
|
|
29732
29988
|
|
|
29989
|
+
// src/settings.ts
|
|
29990
|
+
var DEFAULT_SETTINGS = {
|
|
29991
|
+
edgeStyle: "curved",
|
|
29992
|
+
compactMode: false,
|
|
29993
|
+
predicateDisplay: "icon",
|
|
29994
|
+
showNodeLabels: true,
|
|
29995
|
+
physicsEnabled: true,
|
|
29996
|
+
nodeSize: "medium"
|
|
29997
|
+
};
|
|
29998
|
+
var STORAGE_KEY = "yasgui-graph-plugin-settings";
|
|
29999
|
+
function loadSettings() {
|
|
30000
|
+
try {
|
|
30001
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
30002
|
+
if (stored) {
|
|
30003
|
+
return { ...DEFAULT_SETTINGS, ...JSON.parse(stored) };
|
|
30004
|
+
}
|
|
30005
|
+
} catch (e) {
|
|
30006
|
+
}
|
|
30007
|
+
return { ...DEFAULT_SETTINGS };
|
|
30008
|
+
}
|
|
30009
|
+
function saveSettings(settings) {
|
|
30010
|
+
try {
|
|
30011
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
|
|
30012
|
+
} catch (e) {
|
|
30013
|
+
}
|
|
30014
|
+
}
|
|
30015
|
+
|
|
29733
30016
|
// src/GraphPlugin.ts
|
|
29734
30017
|
var GraphPlugin = class {
|
|
29735
30018
|
constructor(yasr) {
|
|
30019
|
+
this.settingsPanelOpen = false;
|
|
30020
|
+
this.clickOutsideHandler = null;
|
|
29736
30021
|
this.yasr = yasr;
|
|
29737
30022
|
this.network = null;
|
|
29738
30023
|
this.currentTheme = getCurrentTheme();
|
|
@@ -29742,6 +30027,7 @@ var GraphPlugin = class {
|
|
|
29742
30027
|
this.edgesDataSet = null;
|
|
29743
30028
|
this.triples = null;
|
|
29744
30029
|
this.prefixMap = null;
|
|
30030
|
+
this.settings = loadSettings();
|
|
29745
30031
|
}
|
|
29746
30032
|
/**
|
|
29747
30033
|
* Plugin priority (higher = shown first in tabs)
|
|
@@ -29775,6 +30061,7 @@ var GraphPlugin = class {
|
|
|
29775
30061
|
* Render the graph visualization
|
|
29776
30062
|
*/
|
|
29777
30063
|
draw() {
|
|
30064
|
+
const wasPanelOpen = this.settingsPanelOpen;
|
|
29778
30065
|
this.yasr.resultsEl.innerHTML = "";
|
|
29779
30066
|
try {
|
|
29780
30067
|
this.triples = parseConstructResults(this.yasr.results);
|
|
@@ -29791,24 +30078,45 @@ var GraphPlugin = class {
|
|
|
29791
30078
|
this.prefixMap = extractPrefixes(this.yasr);
|
|
29792
30079
|
this.currentTheme = getCurrentTheme();
|
|
29793
30080
|
const themeColors = getThemeNodeColors(this.currentTheme);
|
|
29794
|
-
const { nodes, edges } = triplesToGraph(this.triples, this.prefixMap, themeColors);
|
|
30081
|
+
const { nodes, edges } = triplesToGraph(this.triples, this.prefixMap, themeColors, this.settings);
|
|
29795
30082
|
const container = document.createElement("div");
|
|
29796
30083
|
container.className = "yasgui-graph-plugin-container";
|
|
29797
30084
|
container.id = "yasgui-graph-plugin-container";
|
|
29798
30085
|
this.yasr.resultsEl.appendChild(container);
|
|
29799
30086
|
this.nodesDataSet = new DataSet(nodes);
|
|
29800
30087
|
this.edgesDataSet = new DataSet(edges);
|
|
29801
|
-
const options = getDefaultNetworkOptions(themeColors);
|
|
30088
|
+
const options = getDefaultNetworkOptions(themeColors, this.settings);
|
|
29802
30089
|
this.network = new Network(
|
|
29803
30090
|
container,
|
|
29804
30091
|
{ nodes: this.nodesDataSet, edges: this.edgesDataSet },
|
|
29805
30092
|
options
|
|
29806
30093
|
);
|
|
30094
|
+
this.setupHtmlTooltips(container);
|
|
29807
30095
|
this.applyCanvasBackground(themeColors.background);
|
|
29808
|
-
this.
|
|
29809
|
-
|
|
29810
|
-
this.network.
|
|
29811
|
-
|
|
30096
|
+
this.setupContainerResize(container);
|
|
30097
|
+
if (this.settings.physicsEnabled) {
|
|
30098
|
+
this.network.on("stabilizationIterationsDone", () => {
|
|
30099
|
+
this.network.setOptions({ physics: { enabled: true } });
|
|
30100
|
+
this.network.fit({ maxZoomLevel: 3 });
|
|
30101
|
+
});
|
|
30102
|
+
} else {
|
|
30103
|
+
setTimeout(() => {
|
|
30104
|
+
if (this.network) {
|
|
30105
|
+
this.network.fit({ maxZoomLevel: 3 });
|
|
30106
|
+
}
|
|
30107
|
+
}, 100);
|
|
30108
|
+
}
|
|
30109
|
+
this.network.on("dragEnd", (params) => {
|
|
30110
|
+
if (params.nodes.length > 0) {
|
|
30111
|
+
const positions = this.network.getPositions(params.nodes);
|
|
30112
|
+
const updates = params.nodes.map((id2) => ({
|
|
30113
|
+
id: id2,
|
|
30114
|
+
x: positions[id2].x,
|
|
30115
|
+
y: positions[id2].y,
|
|
30116
|
+
fixed: { x: true, y: true }
|
|
30117
|
+
}));
|
|
30118
|
+
this.nodesDataSet.update(updates);
|
|
30119
|
+
}
|
|
29812
30120
|
});
|
|
29813
30121
|
if (!this.themeObserver) {
|
|
29814
30122
|
this.themeObserver = watchThemeChanges((newTheme) => {
|
|
@@ -29827,6 +30135,20 @@ var GraphPlugin = class {
|
|
|
29827
30135
|
}
|
|
29828
30136
|
};
|
|
29829
30137
|
controls.appendChild(fitButton);
|
|
30138
|
+
const settingsButton = document.createElement("button");
|
|
30139
|
+
settingsButton.className = "yasgui-graph-button yasgui-graph-settings-button";
|
|
30140
|
+
settingsButton.setAttribute("aria-label", "Graph settings");
|
|
30141
|
+
settingsButton.innerHTML = `<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
30142
|
+
<path d="M8 10.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z"/>
|
|
30143
|
+
<path d="M9.796 1.343c-.527-1.79-3.065-1.79-3.592 0l-.094.319a.873.873 0 0 1-1.255.52l-.292-.16c-1.64-.892-3.434.901-2.54 2.541l.159.292a.873.873 0 0 1-.52 1.255l-.319.094c-1.79.527-1.79 3.065 0 3.592l.319.094a.873.873 0 0 1 .52 1.255l-.16.292c-.892 1.64.901 3.434 2.541 2.54l.292-.159a.873.873 0 0 1 1.255.52l.094.319c.527 1.79 3.065 1.79 3.592 0l.094-.319a.873.873 0 0 1 1.255-.52l.292.16c1.64.892 3.434-.901 2.54-2.541l-.159-.292a.873.873 0 0 1 .52-1.255l.319-.094c1.79-.527 1.79-3.065 0-3.592l-.319-.094a.873.873 0 0 1-.52-1.255l.16-.292c.892-1.64-.901-3.434-2.541-2.54l-.292.159a.873.873 0 0 1-1.255-.52l-.094-.319zm-2.633.283c.246-.835 1.428-.835 1.674 0l.094.319a1.873 1.873 0 0 0 2.693 1.115l.291-.16c.764-.415 1.6.42 1.184 1.185l-.159.292a1.873 1.873 0 0 0 1.116 2.692l.318.094c.835.246.835 1.428 0 1.674l-.319.094a1.873 1.873 0 0 0-1.115 2.693l.16.291c.415.764-.42 1.6-1.185 1.184l-.291-.159a1.873 1.873 0 0 0-2.693 1.116l-.094.318c-.246.835-1.428.835-1.674 0l-.094-.319a1.873 1.873 0 0 0-2.692-1.115l-.292.16c-.764.415-1.6-.42-1.184-1.185l.159-.291A1.873 1.873 0 0 0 1.945 8.93l-.319-.094c-.835-.246-.835-1.428 0-1.674l.319-.094A1.873 1.873 0 0 0 3.06 4.474l-.16-.292c-.415-.764.42-1.6 1.185-1.184l.292.159a1.873 1.873 0 0 0 2.692-1.115l.094-.319z"/>
|
|
30144
|
+
</svg> Settings`;
|
|
30145
|
+
settingsButton.onclick = () => {
|
|
30146
|
+
this.toggleSettingsPanel(container);
|
|
30147
|
+
};
|
|
30148
|
+
controls.appendChild(settingsButton);
|
|
30149
|
+
if (wasPanelOpen) {
|
|
30150
|
+
this.toggleSettingsPanel(container);
|
|
30151
|
+
}
|
|
29830
30152
|
} catch (error) {
|
|
29831
30153
|
console.error("Error rendering graph:", error);
|
|
29832
30154
|
const errorDiv = document.createElement("div");
|
|
@@ -29835,6 +30157,111 @@ var GraphPlugin = class {
|
|
|
29835
30157
|
this.yasr.resultsEl.appendChild(errorDiv);
|
|
29836
30158
|
}
|
|
29837
30159
|
}
|
|
30160
|
+
/**
|
|
30161
|
+
* Setup custom HTML tooltip rendering for vis-network
|
|
30162
|
+
* @param container - The graph container element
|
|
30163
|
+
*/
|
|
30164
|
+
setupHtmlTooltips(container) {
|
|
30165
|
+
if (!this.network) return;
|
|
30166
|
+
let hideTimeout = null;
|
|
30167
|
+
this.network.on("hoverNode", (params) => {
|
|
30168
|
+
if (hideTimeout) {
|
|
30169
|
+
clearTimeout(hideTimeout);
|
|
30170
|
+
hideTimeout = null;
|
|
30171
|
+
}
|
|
30172
|
+
const nodeId = params.node;
|
|
30173
|
+
const node = this.nodesDataSet.get(nodeId);
|
|
30174
|
+
if (node && node.title) {
|
|
30175
|
+
this.showHtmlTooltip(container, node.title, params.pointer.DOM);
|
|
30176
|
+
}
|
|
30177
|
+
});
|
|
30178
|
+
this.network.on("hoverEdge", (params) => {
|
|
30179
|
+
if (hideTimeout) {
|
|
30180
|
+
clearTimeout(hideTimeout);
|
|
30181
|
+
hideTimeout = null;
|
|
30182
|
+
}
|
|
30183
|
+
const edgeId = params.edge;
|
|
30184
|
+
const edge = this.edgesDataSet.get(edgeId);
|
|
30185
|
+
if (edge && edge.title) {
|
|
30186
|
+
this.showHtmlTooltip(container, edge.title, params.pointer.DOM);
|
|
30187
|
+
}
|
|
30188
|
+
});
|
|
30189
|
+
this.network.on("blurNode", () => {
|
|
30190
|
+
hideTimeout = window.setTimeout(() => {
|
|
30191
|
+
this.hideHtmlTooltipIfNotHovered(container);
|
|
30192
|
+
}, 200);
|
|
30193
|
+
});
|
|
30194
|
+
this.network.on("blurEdge", () => {
|
|
30195
|
+
hideTimeout = window.setTimeout(() => {
|
|
30196
|
+
this.hideHtmlTooltipIfNotHovered(container);
|
|
30197
|
+
}, 200);
|
|
30198
|
+
});
|
|
30199
|
+
this.network.on("dragStart", () => {
|
|
30200
|
+
if (hideTimeout) {
|
|
30201
|
+
clearTimeout(hideTimeout);
|
|
30202
|
+
hideTimeout = null;
|
|
30203
|
+
}
|
|
30204
|
+
this.hideHtmlTooltip(container);
|
|
30205
|
+
});
|
|
30206
|
+
this.network.on("zoom", () => {
|
|
30207
|
+
if (hideTimeout) {
|
|
30208
|
+
clearTimeout(hideTimeout);
|
|
30209
|
+
hideTimeout = null;
|
|
30210
|
+
}
|
|
30211
|
+
this.hideHtmlTooltip(container);
|
|
30212
|
+
});
|
|
30213
|
+
}
|
|
30214
|
+
/**
|
|
30215
|
+
* Show HTML tooltip at specified position
|
|
30216
|
+
* @param container - Container element
|
|
30217
|
+
* @param htmlContent - HTML content to display
|
|
30218
|
+
* @param position - Mouse position {x, y}
|
|
30219
|
+
*/
|
|
30220
|
+
showHtmlTooltip(container, htmlContent, position) {
|
|
30221
|
+
this.hideHtmlTooltip(container);
|
|
30222
|
+
const tooltip = document.createElement("div");
|
|
30223
|
+
tooltip.className = "yasgui-graph-tooltip-container";
|
|
30224
|
+
tooltip.innerHTML = htmlContent;
|
|
30225
|
+
tooltip.style.position = "absolute";
|
|
30226
|
+
tooltip.style.left = `${position.x + 10}px`;
|
|
30227
|
+
tooltip.style.top = `${position.y + 10}px`;
|
|
30228
|
+
tooltip.style.zIndex = "1000";
|
|
30229
|
+
tooltip.addEventListener("mouseleave", () => {
|
|
30230
|
+
this.hideHtmlTooltip(container);
|
|
30231
|
+
});
|
|
30232
|
+
container.appendChild(tooltip);
|
|
30233
|
+
const rect = tooltip.getBoundingClientRect();
|
|
30234
|
+
const containerRect = container.getBoundingClientRect();
|
|
30235
|
+
if (rect.right > containerRect.right) {
|
|
30236
|
+
tooltip.style.left = `${position.x - rect.width - 10}px`;
|
|
30237
|
+
}
|
|
30238
|
+
if (rect.bottom > containerRect.bottom) {
|
|
30239
|
+
tooltip.style.top = `${position.y - rect.height - 10}px`;
|
|
30240
|
+
}
|
|
30241
|
+
}
|
|
30242
|
+
/**
|
|
30243
|
+
* Hide HTML tooltip
|
|
30244
|
+
* @param container - Container element
|
|
30245
|
+
*/
|
|
30246
|
+
hideHtmlTooltip(container) {
|
|
30247
|
+
const existingTooltip = container.querySelector(".yasgui-graph-tooltip-container");
|
|
30248
|
+
if (existingTooltip) {
|
|
30249
|
+
existingTooltip.remove();
|
|
30250
|
+
}
|
|
30251
|
+
}
|
|
30252
|
+
/**
|
|
30253
|
+
* Hide HTML tooltip only if mouse is not hovering over it
|
|
30254
|
+
* @param container - Container element
|
|
30255
|
+
*/
|
|
30256
|
+
hideHtmlTooltipIfNotHovered(container) {
|
|
30257
|
+
const existingTooltip = container.querySelector(".yasgui-graph-tooltip-container");
|
|
30258
|
+
if (existingTooltip) {
|
|
30259
|
+
const isHovered = existingTooltip.matches(":hover");
|
|
30260
|
+
if (!isHovered) {
|
|
30261
|
+
existingTooltip.remove();
|
|
30262
|
+
}
|
|
30263
|
+
}
|
|
30264
|
+
}
|
|
29838
30265
|
/**
|
|
29839
30266
|
* Apply theme to existing network
|
|
29840
30267
|
* @param newTheme - 'light' or 'dark'
|
|
@@ -29845,12 +30272,30 @@ var GraphPlugin = class {
|
|
|
29845
30272
|
}
|
|
29846
30273
|
this.currentTheme = newTheme;
|
|
29847
30274
|
const themeColors = getThemeNodeColors(newTheme);
|
|
29848
|
-
const
|
|
30275
|
+
const fixedNodes = {};
|
|
30276
|
+
this.nodesDataSet.get().forEach((node) => {
|
|
30277
|
+
var _a, _b;
|
|
30278
|
+
const isFixed = node.fixed === true || typeof node.fixed === "object" && ((_a = node.fixed) == null ? void 0 : _a.x) && ((_b = node.fixed) == null ? void 0 : _b.y);
|
|
30279
|
+
if (isFixed && node.x !== void 0 && node.y !== void 0) {
|
|
30280
|
+
fixedNodes[node.id] = { x: node.x, y: node.y };
|
|
30281
|
+
}
|
|
30282
|
+
});
|
|
30283
|
+
const { nodes, edges } = triplesToGraph(this.triples, this.prefixMap, themeColors, this.settings);
|
|
29849
30284
|
this.nodesDataSet.clear();
|
|
29850
30285
|
this.nodesDataSet.add(nodes);
|
|
29851
30286
|
this.edgesDataSet.clear();
|
|
29852
30287
|
this.edgesDataSet.add(edges);
|
|
29853
|
-
const
|
|
30288
|
+
const fixedIds = Object.keys(fixedNodes);
|
|
30289
|
+
if (fixedIds.length > 0) {
|
|
30290
|
+
const updates = fixedIds.map((id2) => ({
|
|
30291
|
+
id: id2,
|
|
30292
|
+
x: fixedNodes[id2].x,
|
|
30293
|
+
y: fixedNodes[id2].y,
|
|
30294
|
+
fixed: { x: true, y: true }
|
|
30295
|
+
}));
|
|
30296
|
+
this.nodesDataSet.update(updates);
|
|
30297
|
+
}
|
|
30298
|
+
const options = getDefaultNetworkOptions(themeColors, this.settings);
|
|
29854
30299
|
this.network.setOptions(options);
|
|
29855
30300
|
this.applyCanvasBackground(themeColors.background);
|
|
29856
30301
|
}
|
|
@@ -29886,6 +30331,174 @@ var GraphPlugin = class {
|
|
|
29886
30331
|
});
|
|
29887
30332
|
this.resizeObserver.observe(parent2);
|
|
29888
30333
|
}
|
|
30334
|
+
/**
|
|
30335
|
+
* Toggle the settings panel open/closed
|
|
30336
|
+
* @param container - The graph container element
|
|
30337
|
+
*/
|
|
30338
|
+
toggleSettingsPanel(container) {
|
|
30339
|
+
const existing = container.querySelector(".yasgui-graph-settings-panel");
|
|
30340
|
+
if (existing) {
|
|
30341
|
+
existing.remove();
|
|
30342
|
+
this.settingsPanelOpen = false;
|
|
30343
|
+
this.removeClickOutsideHandler();
|
|
30344
|
+
} else {
|
|
30345
|
+
const panel = this.createSettingsPanel(container);
|
|
30346
|
+
container.appendChild(panel);
|
|
30347
|
+
this.settingsPanelOpen = true;
|
|
30348
|
+
this.setupClickOutsideHandler(container, panel);
|
|
30349
|
+
}
|
|
30350
|
+
}
|
|
30351
|
+
/**
|
|
30352
|
+
* Setup click-outside-to-close handler for settings panel
|
|
30353
|
+
* @param container - The graph container element
|
|
30354
|
+
* @param panel - The settings panel element
|
|
30355
|
+
*/
|
|
30356
|
+
setupClickOutsideHandler(container, panel) {
|
|
30357
|
+
this.removeClickOutsideHandler();
|
|
30358
|
+
this.clickOutsideHandler = (event) => {
|
|
30359
|
+
const target = event.target;
|
|
30360
|
+
if (!panel.contains(target) && !this.isSettingsButton(target)) {
|
|
30361
|
+
this.toggleSettingsPanel(container);
|
|
30362
|
+
}
|
|
30363
|
+
};
|
|
30364
|
+
setTimeout(() => {
|
|
30365
|
+
document.addEventListener("click", this.clickOutsideHandler);
|
|
30366
|
+
}, 100);
|
|
30367
|
+
}
|
|
30368
|
+
/**
|
|
30369
|
+
* Remove the click-outside handler
|
|
30370
|
+
*/
|
|
30371
|
+
removeClickOutsideHandler() {
|
|
30372
|
+
if (this.clickOutsideHandler) {
|
|
30373
|
+
document.removeEventListener("click", this.clickOutsideHandler);
|
|
30374
|
+
this.clickOutsideHandler = null;
|
|
30375
|
+
}
|
|
30376
|
+
}
|
|
30377
|
+
/**
|
|
30378
|
+
* Check if a node is the settings button or inside it
|
|
30379
|
+
* @param node - The node to check
|
|
30380
|
+
*/
|
|
30381
|
+
isSettingsButton(node) {
|
|
30382
|
+
let current = node;
|
|
30383
|
+
while (current) {
|
|
30384
|
+
if (current instanceof Element && current.classList.contains("yasgui-graph-settings-button")) {
|
|
30385
|
+
return true;
|
|
30386
|
+
}
|
|
30387
|
+
current = current.parentNode;
|
|
30388
|
+
}
|
|
30389
|
+
return false;
|
|
30390
|
+
}
|
|
30391
|
+
/**
|
|
30392
|
+
* Build and return the settings panel element
|
|
30393
|
+
* @param container - The graph container element (used to re-draw on change)
|
|
30394
|
+
*/
|
|
30395
|
+
createSettingsPanel(_container) {
|
|
30396
|
+
const panel = document.createElement("div");
|
|
30397
|
+
panel.className = "yasgui-graph-settings-panel";
|
|
30398
|
+
panel.setAttribute("role", "dialog");
|
|
30399
|
+
panel.setAttribute("aria-label", "Graph settings");
|
|
30400
|
+
const title = document.createElement("div");
|
|
30401
|
+
title.className = "yasgui-graph-settings-title";
|
|
30402
|
+
title.textContent = "Graph Settings";
|
|
30403
|
+
panel.appendChild(title);
|
|
30404
|
+
const addSection = (label) => {
|
|
30405
|
+
const h = document.createElement("div");
|
|
30406
|
+
h.className = "yasgui-graph-settings-section";
|
|
30407
|
+
h.textContent = label;
|
|
30408
|
+
panel.appendChild(h);
|
|
30409
|
+
};
|
|
30410
|
+
const addToggle = (label, checked, onChange) => {
|
|
30411
|
+
const row = document.createElement("label");
|
|
30412
|
+
row.className = "yasgui-graph-settings-row";
|
|
30413
|
+
const input = document.createElement("input");
|
|
30414
|
+
input.type = "checkbox";
|
|
30415
|
+
input.checked = checked;
|
|
30416
|
+
input.addEventListener("change", () => onChange(input.checked));
|
|
30417
|
+
const span = document.createElement("span");
|
|
30418
|
+
span.textContent = label;
|
|
30419
|
+
row.appendChild(input);
|
|
30420
|
+
row.appendChild(span);
|
|
30421
|
+
panel.appendChild(row);
|
|
30422
|
+
};
|
|
30423
|
+
const addSelect = (label, options, current, onChange) => {
|
|
30424
|
+
const row = document.createElement("div");
|
|
30425
|
+
row.className = "yasgui-graph-settings-row";
|
|
30426
|
+
const lbl = document.createElement("span");
|
|
30427
|
+
lbl.textContent = label;
|
|
30428
|
+
const sel = document.createElement("select");
|
|
30429
|
+
sel.className = "yasgui-graph-settings-select";
|
|
30430
|
+
options.forEach((o) => {
|
|
30431
|
+
const opt = document.createElement("option");
|
|
30432
|
+
opt.value = o.value;
|
|
30433
|
+
opt.textContent = o.label;
|
|
30434
|
+
if (o.value === current) opt.selected = true;
|
|
30435
|
+
sel.appendChild(opt);
|
|
30436
|
+
});
|
|
30437
|
+
sel.addEventListener("change", () => onChange(sel.value));
|
|
30438
|
+
row.appendChild(lbl);
|
|
30439
|
+
row.appendChild(sel);
|
|
30440
|
+
panel.appendChild(row);
|
|
30441
|
+
};
|
|
30442
|
+
const applyAndRedraw = () => {
|
|
30443
|
+
saveSettings(this.settings);
|
|
30444
|
+
this.draw();
|
|
30445
|
+
};
|
|
30446
|
+
addSection("Arrows");
|
|
30447
|
+
addSelect(
|
|
30448
|
+
"Style",
|
|
30449
|
+
[
|
|
30450
|
+
{ value: "curved", label: "Curved" },
|
|
30451
|
+
{ value: "straight", label: "Straight" }
|
|
30452
|
+
],
|
|
30453
|
+
this.settings.edgeStyle,
|
|
30454
|
+
(v) => {
|
|
30455
|
+
this.settings.edgeStyle = v;
|
|
30456
|
+
applyAndRedraw();
|
|
30457
|
+
}
|
|
30458
|
+
);
|
|
30459
|
+
addSection("Predicate display");
|
|
30460
|
+
addSelect(
|
|
30461
|
+
"Display",
|
|
30462
|
+
[
|
|
30463
|
+
{ value: "label", label: "Label (prefixed URI)" },
|
|
30464
|
+
{ value: "icon", label: "Icon / symbol" },
|
|
30465
|
+
{ value: "none", label: "Hidden" }
|
|
30466
|
+
],
|
|
30467
|
+
this.settings.predicateDisplay,
|
|
30468
|
+
(v) => {
|
|
30469
|
+
this.settings.predicateDisplay = v;
|
|
30470
|
+
applyAndRedraw();
|
|
30471
|
+
}
|
|
30472
|
+
);
|
|
30473
|
+
addSection("Compact mode");
|
|
30474
|
+
addToggle("Compact mode", this.settings.compactMode, (v) => {
|
|
30475
|
+
this.settings.compactMode = v;
|
|
30476
|
+
applyAndRedraw();
|
|
30477
|
+
});
|
|
30478
|
+
addSection("Display");
|
|
30479
|
+
addToggle("Show node labels", this.settings.showNodeLabels, (v) => {
|
|
30480
|
+
this.settings.showNodeLabels = v;
|
|
30481
|
+
applyAndRedraw();
|
|
30482
|
+
});
|
|
30483
|
+
addToggle("Enable physics", this.settings.physicsEnabled, (v) => {
|
|
30484
|
+
this.settings.physicsEnabled = v;
|
|
30485
|
+
applyAndRedraw();
|
|
30486
|
+
});
|
|
30487
|
+
addSelect(
|
|
30488
|
+
"Node size",
|
|
30489
|
+
[
|
|
30490
|
+
{ value: "small", label: "Small" },
|
|
30491
|
+
{ value: "medium", label: "Medium" },
|
|
30492
|
+
{ value: "large", label: "Large" }
|
|
30493
|
+
],
|
|
30494
|
+
this.settings.nodeSize,
|
|
30495
|
+
(v) => {
|
|
30496
|
+
this.settings.nodeSize = v;
|
|
30497
|
+
applyAndRedraw();
|
|
30498
|
+
}
|
|
30499
|
+
);
|
|
30500
|
+
return panel;
|
|
30501
|
+
}
|
|
29889
30502
|
/**
|
|
29890
30503
|
* Get icon for plugin tab
|
|
29891
30504
|
* @returns Icon element
|
|
@@ -29908,6 +30521,7 @@ var GraphPlugin = class {
|
|
|
29908
30521
|
* Cleanup when plugin is destroyed
|
|
29909
30522
|
*/
|
|
29910
30523
|
destroy() {
|
|
30524
|
+
this.removeClickOutsideHandler();
|
|
29911
30525
|
if (this.themeObserver) {
|
|
29912
30526
|
this.themeObserver.disconnect();
|
|
29913
30527
|
this.themeObserver = null;
|