@matdata/yasgui-graph-plugin 1.4.1 → 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 +143 -26
- 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 +159 -35
- package/dist/yasgui-graph-plugin.esm.css +143 -26
- 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
|
@@ -41,14 +41,18 @@ function truncateLabel(text, maxLength = 50) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
// src/networkConfig.ts
|
|
44
|
-
function getDefaultNetworkOptions(themeColors) {
|
|
44
|
+
function getDefaultNetworkOptions(themeColors, settings) {
|
|
45
|
+
const curved = !settings || settings.edgeStyle !== "straight";
|
|
46
|
+
const nodeSizeMap = { small: 6, medium: 10, large: 16 };
|
|
47
|
+
const nodeSize = (settings == null ? void 0 : settings.nodeSize) ? nodeSizeMap[settings.nodeSize] : 10;
|
|
48
|
+
const showNodeLabels = (settings == null ? void 0 : settings.showNodeLabels) !== false;
|
|
45
49
|
return {
|
|
46
50
|
// Configure canvas background color based on theme
|
|
47
51
|
configure: {
|
|
48
52
|
enabled: false
|
|
49
53
|
},
|
|
50
54
|
physics: {
|
|
51
|
-
enabled:
|
|
55
|
+
enabled: (settings == null ? void 0 : settings.physicsEnabled) !== false,
|
|
52
56
|
stabilization: {
|
|
53
57
|
enabled: true,
|
|
54
58
|
iterations: 200,
|
|
@@ -68,14 +72,16 @@ function getDefaultNetworkOptions(themeColors) {
|
|
|
68
72
|
dragView: true,
|
|
69
73
|
zoomView: true,
|
|
70
74
|
hover: true,
|
|
71
|
-
tooltipDelay: 300
|
|
75
|
+
tooltipDelay: 300,
|
|
72
76
|
// 300ms hover delay per spec
|
|
77
|
+
hideEdgesOnDrag: false,
|
|
78
|
+
hideEdgesOnZoom: false
|
|
73
79
|
},
|
|
74
80
|
nodes: {
|
|
75
81
|
shape: "dot",
|
|
76
|
-
size:
|
|
82
|
+
size: nodeSize,
|
|
77
83
|
font: {
|
|
78
|
-
size: 12,
|
|
84
|
+
size: showNodeLabels ? 12 : 0,
|
|
79
85
|
color: themeColors.text
|
|
80
86
|
},
|
|
81
87
|
borderWidth: 1,
|
|
@@ -90,7 +96,7 @@ function getDefaultNetworkOptions(themeColors) {
|
|
|
90
96
|
}
|
|
91
97
|
},
|
|
92
98
|
smooth: {
|
|
93
|
-
enabled:
|
|
99
|
+
enabled: curved,
|
|
94
100
|
type: "dynamic",
|
|
95
101
|
roundness: 0.5
|
|
96
102
|
},
|
|
@@ -152,10 +158,129 @@ function getNodeColor(node, triples, themeColors) {
|
|
|
152
158
|
return themeColors.uri;
|
|
153
159
|
}
|
|
154
160
|
|
|
161
|
+
// src/predicateIcons.ts
|
|
162
|
+
var PREDICATE_ICONS = {
|
|
163
|
+
// RDF core
|
|
164
|
+
"http://www.w3.org/1999/02/22-rdf-syntax-ns#type": "a",
|
|
165
|
+
// Turtle's "a" shorthand
|
|
166
|
+
"http://www.w3.org/1999/02/22-rdf-syntax-ns#value": "val",
|
|
167
|
+
// RDFS
|
|
168
|
+
"http://www.w3.org/2000/01/rdf-schema#label": "lbl",
|
|
169
|
+
"http://www.w3.org/2000/01/rdf-schema#comment": "cmt",
|
|
170
|
+
"http://www.w3.org/2000/01/rdf-schema#subClassOf": "\u2282",
|
|
171
|
+
// ⊂
|
|
172
|
+
"http://www.w3.org/2000/01/rdf-schema#subPropertyOf": "\u2286",
|
|
173
|
+
// ⊆
|
|
174
|
+
"http://www.w3.org/2000/01/rdf-schema#domain": "dom",
|
|
175
|
+
"http://www.w3.org/2000/01/rdf-schema#range": "rng",
|
|
176
|
+
"http://www.w3.org/2000/01/rdf-schema#seeAlso": "see",
|
|
177
|
+
"http://www.w3.org/2000/01/rdf-schema#isDefinedBy": "idb",
|
|
178
|
+
// OWL
|
|
179
|
+
"http://www.w3.org/2002/07/owl#sameAs": "\u2261",
|
|
180
|
+
// ≡
|
|
181
|
+
"http://www.w3.org/2002/07/owl#equivalentClass": "\u2245",
|
|
182
|
+
// ≅
|
|
183
|
+
"http://www.w3.org/2002/07/owl#inverseOf": "\u21C4",
|
|
184
|
+
// ⇄
|
|
185
|
+
"http://www.w3.org/2002/07/owl#disjointWith": "\u2260",
|
|
186
|
+
// ≠
|
|
187
|
+
// SKOS
|
|
188
|
+
"http://www.w3.org/2004/02/skos/core#prefLabel": "\u2605",
|
|
189
|
+
// ★
|
|
190
|
+
"http://www.w3.org/2004/02/skos/core#altLabel": "\u2606",
|
|
191
|
+
// ☆
|
|
192
|
+
"http://www.w3.org/2004/02/skos/core#definition": "def",
|
|
193
|
+
"http://www.w3.org/2004/02/skos/core#broader": "\u2191",
|
|
194
|
+
// ↑
|
|
195
|
+
"http://www.w3.org/2004/02/skos/core#narrower": "\u2193",
|
|
196
|
+
// ↓
|
|
197
|
+
"http://www.w3.org/2004/02/skos/core#related": "\u2194",
|
|
198
|
+
// ↔
|
|
199
|
+
"http://www.w3.org/2004/02/skos/core#note": "note",
|
|
200
|
+
"http://www.w3.org/2004/02/skos/core#exactMatch": "\u2261",
|
|
201
|
+
// ≡
|
|
202
|
+
"http://www.w3.org/2004/02/skos/core#closeMatch": "\u2248",
|
|
203
|
+
// ≈
|
|
204
|
+
// Dublin Core Terms
|
|
205
|
+
"http://purl.org/dc/terms/title": "ttl",
|
|
206
|
+
"http://purl.org/dc/terms/description": "dsc",
|
|
207
|
+
"http://purl.org/dc/terms/created": "crt",
|
|
208
|
+
"http://purl.org/dc/terms/modified": "mod",
|
|
209
|
+
"http://purl.org/dc/terms/creator": "by",
|
|
210
|
+
"http://purl.org/dc/terms/subject": "sbj",
|
|
211
|
+
// FOAF
|
|
212
|
+
"http://xmlns.com/foaf/0.1/name": "nm",
|
|
213
|
+
"http://xmlns.com/foaf/0.1/knows": "\u27F7",
|
|
214
|
+
// ⟷
|
|
215
|
+
"http://xmlns.com/foaf/0.1/member": "mbr",
|
|
216
|
+
// Schema.org
|
|
217
|
+
"http://schema.org/name": "nm",
|
|
218
|
+
"http://schema.org/description": "dsc"
|
|
219
|
+
};
|
|
220
|
+
function getPredicateIcon(predicateUri) {
|
|
221
|
+
return PREDICATE_ICONS[predicateUri];
|
|
222
|
+
}
|
|
223
|
+
|
|
155
224
|
// src/transformers.ts
|
|
156
|
-
|
|
225
|
+
var RDF_TYPE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";
|
|
226
|
+
var SCHEMA_IMAGE = "https://schema.org/image";
|
|
227
|
+
var SCHEMA_ICON = "https://schema.org/icon";
|
|
228
|
+
var RDFS_LABEL = "http://www.w3.org/2000/01/rdf-schema#label";
|
|
229
|
+
var RDFS_SUBCLASSOF = "http://www.w3.org/2000/01/rdf-schema#subClassOf";
|
|
230
|
+
var SUPPRESSED_PREDICATES = /* @__PURE__ */ new Set([SCHEMA_IMAGE, SCHEMA_ICON, RDFS_LABEL]);
|
|
231
|
+
function appendTooltipRows(title, rows) {
|
|
232
|
+
const closingTag = "</div>";
|
|
233
|
+
const idx = title.lastIndexOf(closingTag);
|
|
234
|
+
if (idx === -1) return title + rows;
|
|
235
|
+
return title.slice(0, idx) + rows + closingTag;
|
|
236
|
+
}
|
|
237
|
+
function buildVisualTooltipRow(key, value) {
|
|
238
|
+
return `<div class="yasgui-tooltip-row"><span class="yasgui-tooltip-key">${escapeHtml(key)}</span><span class="yasgui-tooltip-val">${escapeHtml(value)}</span></div>`;
|
|
239
|
+
}
|
|
240
|
+
function escapeHtml(str) {
|
|
241
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
242
|
+
}
|
|
243
|
+
function createCompactNodeTooltipHTML(uri, triples, prefixMap) {
|
|
244
|
+
const isBlankNode = uri.startsWith("_:");
|
|
245
|
+
let rows = `<div class="yasgui-tooltip-type">${isBlankNode ? "Blank Node" : "URI"}</div>`;
|
|
246
|
+
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>`;
|
|
247
|
+
triples.filter((t) => t.subject === uri && t.predicate === RDF_TYPE).forEach((t) => {
|
|
248
|
+
const typeLabel = applyPrefix(t.object.value, prefixMap);
|
|
249
|
+
rows += `<div class="yasgui-tooltip-row"><span class="yasgui-tooltip-key">rdf:type</span><span class="yasgui-tooltip-val">${escapeHtml(typeLabel)}</span></div>`;
|
|
250
|
+
});
|
|
251
|
+
triples.filter((t) => t.subject === uri && t.object.type === "literal").forEach((t) => {
|
|
252
|
+
const predLabel = applyPrefix(t.predicate, prefixMap);
|
|
253
|
+
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>`;
|
|
254
|
+
});
|
|
255
|
+
return `<div class="yasgui-graph-tooltip">${rows}</div>`;
|
|
256
|
+
}
|
|
257
|
+
function createNodeTooltipHTML(nodeType, value, datatype, lang, prefixMap) {
|
|
258
|
+
const typeLabel = nodeType === "uri" ? "URI" : nodeType === "literal" ? "Literal" : "Blank Node";
|
|
259
|
+
let rows = `<div class="yasgui-tooltip-type">${typeLabel}</div>`;
|
|
260
|
+
if (nodeType === "literal") {
|
|
261
|
+
rows += `<div class="yasgui-tooltip-row"><span class="yasgui-tooltip-key">Value</span><span class="yasgui-tooltip-val">${escapeHtml(value)}</span></div>`;
|
|
262
|
+
if (datatype) {
|
|
263
|
+
const dtLabel = prefixMap ? applyPrefix(datatype, prefixMap) : datatype;
|
|
264
|
+
rows += `<div class="yasgui-tooltip-row"><span class="yasgui-tooltip-key">Datatype</span><span class="yasgui-tooltip-val">${escapeHtml(dtLabel)}</span></div>`;
|
|
265
|
+
}
|
|
266
|
+
if (lang) {
|
|
267
|
+
rows += `<div class="yasgui-tooltip-row"><span class="yasgui-tooltip-key">Language</span><span class="yasgui-tooltip-val">${escapeHtml(lang)}</span></div>`;
|
|
268
|
+
}
|
|
269
|
+
} else if (nodeType === "uri") {
|
|
270
|
+
rows += `<div class="yasgui-tooltip-row"><span class="yasgui-tooltip-key">Full URI</span><span class="yasgui-tooltip-val">${escapeHtml(value)}</span></div>`;
|
|
271
|
+
} else if (nodeType === "bnode") {
|
|
272
|
+
rows += `<div class="yasgui-tooltip-row"><span class="yasgui-tooltip-key">Identifier</span><span class="yasgui-tooltip-val">${escapeHtml(value)}</span></div>`;
|
|
273
|
+
}
|
|
274
|
+
return `<div class="yasgui-graph-tooltip">${rows}</div>`;
|
|
275
|
+
}
|
|
276
|
+
function createEdgeTooltipHTML(predicateUri) {
|
|
277
|
+
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>`;
|
|
278
|
+
return `<div class="yasgui-graph-tooltip">${rows}</div>`;
|
|
279
|
+
}
|
|
280
|
+
function createNodeMap(triples, prefixMap, themeColors, settings) {
|
|
157
281
|
const nodeMap = /* @__PURE__ */ new Map();
|
|
158
282
|
let nodeId = 1;
|
|
283
|
+
const sizeMultiplier = (settings == null ? void 0 : settings.nodeSize) === "small" ? 0.5 : (settings == null ? void 0 : settings.nodeSize) === "large" ? 2 : 1;
|
|
159
284
|
triples.forEach((triple) => {
|
|
160
285
|
if (!nodeMap.has(triple.subject)) {
|
|
161
286
|
const isBlankNode = triple.subject.startsWith("_:");
|
|
@@ -167,11 +292,18 @@ function createNodeMap(triples, prefixMap, themeColors) {
|
|
|
167
292
|
color: getNodeColor({ uri: triple.subject, type: "uri" }, triples, themeColors),
|
|
168
293
|
type: "uri",
|
|
169
294
|
fullValue: triple.subject,
|
|
170
|
-
|
|
295
|
+
size: 10 * sizeMultiplier,
|
|
296
|
+
title: createNodeTooltipHTML(
|
|
297
|
+
isBlankNode ? "bnode" : "uri",
|
|
298
|
+
triple.subject,
|
|
299
|
+
void 0,
|
|
300
|
+
void 0,
|
|
301
|
+
prefixMap
|
|
302
|
+
)
|
|
171
303
|
});
|
|
172
304
|
}
|
|
173
305
|
const objValue = triple.object.value;
|
|
174
|
-
if (!nodeMap.has(objValue)) {
|
|
306
|
+
if (!nodeMap.has(objValue) && !SUPPRESSED_PREDICATES.has(triple.predicate)) {
|
|
175
307
|
const isLiteral = triple.object.type === "literal";
|
|
176
308
|
const isBlankNode = !isLiteral && objValue.startsWith("_:");
|
|
177
309
|
let label;
|
|
@@ -180,15 +312,21 @@ function createNodeMap(triples, prefixMap, themeColors) {
|
|
|
180
312
|
if (isLiteral) {
|
|
181
313
|
label = truncateLabel(objValue);
|
|
182
314
|
fullValue = objValue;
|
|
183
|
-
title =
|
|
315
|
+
title = createNodeTooltipHTML(
|
|
316
|
+
"literal",
|
|
317
|
+
objValue,
|
|
318
|
+
triple.object.datatype,
|
|
319
|
+
triple.object.lang,
|
|
320
|
+
prefixMap
|
|
321
|
+
);
|
|
184
322
|
} else if (isBlankNode) {
|
|
185
323
|
label = objValue;
|
|
186
324
|
fullValue = objValue;
|
|
187
|
-
title = objValue;
|
|
325
|
+
title = createNodeTooltipHTML("bnode", objValue);
|
|
188
326
|
} else {
|
|
189
327
|
label = truncateLabel(applyPrefix(objValue, prefixMap));
|
|
190
328
|
fullValue = objValue;
|
|
191
|
-
title =
|
|
329
|
+
title = createNodeTooltipHTML("uri", objValue, void 0, void 0, prefixMap);
|
|
192
330
|
}
|
|
193
331
|
nodeMap.set(objValue, {
|
|
194
332
|
id: nodeId++,
|
|
@@ -201,39 +339,157 @@ function createNodeMap(triples, prefixMap, themeColors) {
|
|
|
201
339
|
),
|
|
202
340
|
type: isLiteral ? "literal" : "uri",
|
|
203
341
|
fullValue,
|
|
342
|
+
size: (isLiteral ? 5 : 10) * sizeMultiplier,
|
|
204
343
|
title
|
|
205
344
|
});
|
|
206
345
|
}
|
|
207
346
|
});
|
|
208
347
|
return nodeMap;
|
|
209
348
|
}
|
|
210
|
-
function createEdgesArray(triples, nodeMap, prefixMap) {
|
|
349
|
+
function createEdgesArray(triples, nodeMap, prefixMap, settings) {
|
|
211
350
|
const edges = [];
|
|
212
351
|
const edgeSet = /* @__PURE__ */ new Set();
|
|
213
352
|
triples.forEach((triple) => {
|
|
353
|
+
var _a, _b;
|
|
354
|
+
if (SUPPRESSED_PREDICATES.has(triple.predicate)) return;
|
|
214
355
|
const fromNode = nodeMap.get(triple.subject);
|
|
215
356
|
const toNode = nodeMap.get(triple.object.value);
|
|
216
357
|
if (!fromNode || !toNode) return;
|
|
217
358
|
const edgeKey = `${fromNode.id}-${triple.predicate}-${toNode.id}`;
|
|
218
359
|
if (!edgeSet.has(edgeKey)) {
|
|
219
360
|
edgeSet.add(edgeKey);
|
|
361
|
+
let edgeLabel;
|
|
362
|
+
const predicateDisplay = (_a = settings == null ? void 0 : settings.predicateDisplay) != null ? _a : "label";
|
|
363
|
+
if (predicateDisplay === "none") {
|
|
364
|
+
edgeLabel = "";
|
|
365
|
+
} else if (predicateDisplay === "icon") {
|
|
366
|
+
edgeLabel = (_b = getPredicateIcon(triple.predicate)) != null ? _b : truncateLabel(applyPrefix(triple.predicate, prefixMap));
|
|
367
|
+
} else {
|
|
368
|
+
edgeLabel = truncateLabel(applyPrefix(triple.predicate, prefixMap));
|
|
369
|
+
}
|
|
220
370
|
edges.push({
|
|
221
371
|
id: `edge_${fromNode.id}_${toNode.id}_${edges.length}`,
|
|
222
372
|
from: fromNode.id,
|
|
223
373
|
to: toNode.id,
|
|
224
|
-
label:
|
|
374
|
+
label: edgeLabel,
|
|
225
375
|
predicate: triple.predicate,
|
|
226
|
-
title:
|
|
376
|
+
title: createEdgeTooltipHTML(triple.predicate),
|
|
227
377
|
arrows: "to"
|
|
228
378
|
});
|
|
229
379
|
}
|
|
230
380
|
});
|
|
231
381
|
return edges;
|
|
232
382
|
}
|
|
233
|
-
function
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
383
|
+
function isNodeVisible(node, triples, settings) {
|
|
384
|
+
if (node.uri && node.uri.startsWith("_:")) {
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
if (!settings.compactMode) {
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
if (node.type === "literal") {
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
const isClass = triples.some(
|
|
394
|
+
(t) => t.predicate === "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" && t.object.value === node.uri
|
|
395
|
+
);
|
|
396
|
+
if (isClass) {
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
401
|
+
function triplesToGraph(triples, prefixMap, themeColors, settings) {
|
|
402
|
+
const nodeMap = createNodeMap(triples, prefixMap, themeColors, settings);
|
|
403
|
+
const sizeMultiplier = (settings == null ? void 0 : settings.nodeSize) === "small" ? 0.5 : (settings == null ? void 0 : settings.nodeSize) === "large" ? 2 : 1;
|
|
404
|
+
const tripleIndex = /* @__PURE__ */ new Map();
|
|
405
|
+
for (const t of triples) {
|
|
406
|
+
if (!tripleIndex.has(t.subject)) tripleIndex.set(t.subject, /* @__PURE__ */ new Map());
|
|
407
|
+
const sp = tripleIndex.get(t.subject);
|
|
408
|
+
if (!sp.has(t.predicate)) sp.set(t.predicate, []);
|
|
409
|
+
sp.get(t.predicate).push(t.object.value);
|
|
410
|
+
}
|
|
411
|
+
function getNodeVisualIdx(uri) {
|
|
412
|
+
const sp = tripleIndex.get(uri);
|
|
413
|
+
if (!sp) return {};
|
|
414
|
+
const icons = sp.get(SCHEMA_ICON);
|
|
415
|
+
if (icons == null ? void 0 : icons.length) return { icon: icons[0] };
|
|
416
|
+
const images = sp.get(SCHEMA_IMAGE);
|
|
417
|
+
if (images == null ? void 0 : images.length) return { image: images[0] };
|
|
418
|
+
return {};
|
|
419
|
+
}
|
|
420
|
+
function resolveCompactVisualIdx(uri) {
|
|
421
|
+
var _a, _b, _c, _d;
|
|
422
|
+
const own = getNodeVisualIdx(uri);
|
|
423
|
+
if (own.icon || own.image) return own;
|
|
424
|
+
const typeUris = (_b = (_a = tripleIndex.get(uri)) == null ? void 0 : _a.get(RDF_TYPE)) != null ? _b : [];
|
|
425
|
+
for (const typeUri of typeUris) {
|
|
426
|
+
const cv = getNodeVisualIdx(typeUri);
|
|
427
|
+
if (cv.icon || cv.image) return cv;
|
|
428
|
+
}
|
|
429
|
+
for (const typeUri of typeUris) {
|
|
430
|
+
const superUris = (_d = (_c = tripleIndex.get(typeUri)) == null ? void 0 : _c.get(RDFS_SUBCLASSOF)) != null ? _d : [];
|
|
431
|
+
for (const superUri of superUris) {
|
|
432
|
+
const sv = getNodeVisualIdx(superUri);
|
|
433
|
+
if (sv.icon || sv.image) return sv;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return {};
|
|
437
|
+
}
|
|
438
|
+
if (settings == null ? void 0 : settings.compactMode) {
|
|
439
|
+
const subjects = new Set(triples.map((t) => t.subject));
|
|
440
|
+
subjects.forEach((subjectUri) => {
|
|
441
|
+
const node = nodeMap.get(subjectUri);
|
|
442
|
+
if (node) {
|
|
443
|
+
node.title = createCompactNodeTooltipHTML(subjectUri, triples, prefixMap);
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
nodeMap.forEach((node) => {
|
|
448
|
+
var _a, _b;
|
|
449
|
+
if (!node.uri || node.type === "literal") return;
|
|
450
|
+
const visual = (settings == null ? void 0 : settings.compactMode) ? resolveCompactVisualIdx(node.uri) : getNodeVisualIdx(node.uri);
|
|
451
|
+
const rdfsLabel = (_b = (_a = tripleIndex.get(node.uri)) == null ? void 0 : _a.get(RDFS_LABEL)) == null ? void 0 : _b[0];
|
|
452
|
+
if (visual.icon) {
|
|
453
|
+
node.shape = "text";
|
|
454
|
+
node.label = rdfsLabel ? `${visual.icon}
|
|
455
|
+
${rdfsLabel}` : visual.icon;
|
|
456
|
+
node.font = { size: 14 * sizeMultiplier };
|
|
457
|
+
if (!(settings == null ? void 0 : settings.compactMode)) {
|
|
458
|
+
node.title = appendTooltipRows(node.title, buildVisualTooltipRow("Icon", visual.icon));
|
|
459
|
+
}
|
|
460
|
+
} else if (visual.image) {
|
|
461
|
+
let imageAllowed = false;
|
|
462
|
+
try {
|
|
463
|
+
const parsed = new URL(visual.image);
|
|
464
|
+
imageAllowed = parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
465
|
+
} catch (e) {
|
|
466
|
+
}
|
|
467
|
+
if (imageAllowed) {
|
|
468
|
+
node.shape = "circularImage";
|
|
469
|
+
node.image = visual.image;
|
|
470
|
+
}
|
|
471
|
+
if (rdfsLabel) {
|
|
472
|
+
node.label = rdfsLabel;
|
|
473
|
+
node.font = { size: 14 * sizeMultiplier };
|
|
474
|
+
}
|
|
475
|
+
if (!(settings == null ? void 0 : settings.compactMode)) {
|
|
476
|
+
node.title = appendTooltipRows(node.title, buildVisualTooltipRow("Image", visual.image));
|
|
477
|
+
}
|
|
478
|
+
} else if (rdfsLabel) {
|
|
479
|
+
node.label = rdfsLabel;
|
|
480
|
+
}
|
|
481
|
+
if (rdfsLabel && !(settings == null ? void 0 : settings.compactMode)) {
|
|
482
|
+
node.title = appendTooltipRows(node.title, buildVisualTooltipRow("rdfs:label", rdfsLabel));
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
const visibleNodeIds = /* @__PURE__ */ new Set();
|
|
486
|
+
nodeMap.forEach((node) => {
|
|
487
|
+
if (!settings || isNodeVisible(node, triples, settings)) {
|
|
488
|
+
visibleNodeIds.add(node.id);
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
const edges = createEdgesArray(triples, nodeMap, prefixMap, settings).filter((e) => visibleNodeIds.has(e.from) && visibleNodeIds.has(e.to));
|
|
492
|
+
const nodes = Array.from(nodeMap.values()).filter((n) => visibleNodeIds.has(n.id));
|
|
237
493
|
return { nodes, edges };
|
|
238
494
|
}
|
|
239
495
|
|
|
@@ -29704,9 +29960,38 @@ function watchThemeChanges(callback) {
|
|
|
29704
29960
|
return observer;
|
|
29705
29961
|
}
|
|
29706
29962
|
|
|
29963
|
+
// src/settings.ts
|
|
29964
|
+
var DEFAULT_SETTINGS = {
|
|
29965
|
+
edgeStyle: "curved",
|
|
29966
|
+
compactMode: false,
|
|
29967
|
+
predicateDisplay: "icon",
|
|
29968
|
+
showNodeLabels: true,
|
|
29969
|
+
physicsEnabled: true,
|
|
29970
|
+
nodeSize: "medium"
|
|
29971
|
+
};
|
|
29972
|
+
var STORAGE_KEY = "yasgui-graph-plugin-settings";
|
|
29973
|
+
function loadSettings() {
|
|
29974
|
+
try {
|
|
29975
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
29976
|
+
if (stored) {
|
|
29977
|
+
return { ...DEFAULT_SETTINGS, ...JSON.parse(stored) };
|
|
29978
|
+
}
|
|
29979
|
+
} catch (e) {
|
|
29980
|
+
}
|
|
29981
|
+
return { ...DEFAULT_SETTINGS };
|
|
29982
|
+
}
|
|
29983
|
+
function saveSettings(settings) {
|
|
29984
|
+
try {
|
|
29985
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
|
|
29986
|
+
} catch (e) {
|
|
29987
|
+
}
|
|
29988
|
+
}
|
|
29989
|
+
|
|
29707
29990
|
// src/GraphPlugin.ts
|
|
29708
29991
|
var GraphPlugin = class {
|
|
29709
29992
|
constructor(yasr) {
|
|
29993
|
+
this.settingsPanelOpen = false;
|
|
29994
|
+
this.clickOutsideHandler = null;
|
|
29710
29995
|
this.yasr = yasr;
|
|
29711
29996
|
this.network = null;
|
|
29712
29997
|
this.currentTheme = getCurrentTheme();
|
|
@@ -29716,6 +30001,7 @@ var GraphPlugin = class {
|
|
|
29716
30001
|
this.edgesDataSet = null;
|
|
29717
30002
|
this.triples = null;
|
|
29718
30003
|
this.prefixMap = null;
|
|
30004
|
+
this.settings = loadSettings();
|
|
29719
30005
|
}
|
|
29720
30006
|
/**
|
|
29721
30007
|
* Plugin priority (higher = shown first in tabs)
|
|
@@ -29749,6 +30035,7 @@ var GraphPlugin = class {
|
|
|
29749
30035
|
* Render the graph visualization
|
|
29750
30036
|
*/
|
|
29751
30037
|
draw() {
|
|
30038
|
+
const wasPanelOpen = this.settingsPanelOpen;
|
|
29752
30039
|
this.yasr.resultsEl.innerHTML = "";
|
|
29753
30040
|
try {
|
|
29754
30041
|
this.triples = parseConstructResults(this.yasr.results);
|
|
@@ -29765,24 +30052,45 @@ var GraphPlugin = class {
|
|
|
29765
30052
|
this.prefixMap = extractPrefixes(this.yasr);
|
|
29766
30053
|
this.currentTheme = getCurrentTheme();
|
|
29767
30054
|
const themeColors = getThemeNodeColors(this.currentTheme);
|
|
29768
|
-
const { nodes, edges } = triplesToGraph(this.triples, this.prefixMap, themeColors);
|
|
30055
|
+
const { nodes, edges } = triplesToGraph(this.triples, this.prefixMap, themeColors, this.settings);
|
|
29769
30056
|
const container = document.createElement("div");
|
|
29770
30057
|
container.className = "yasgui-graph-plugin-container";
|
|
29771
30058
|
container.id = "yasgui-graph-plugin-container";
|
|
29772
30059
|
this.yasr.resultsEl.appendChild(container);
|
|
29773
30060
|
this.nodesDataSet = new DataSet(nodes);
|
|
29774
30061
|
this.edgesDataSet = new DataSet(edges);
|
|
29775
|
-
const options = getDefaultNetworkOptions(themeColors);
|
|
30062
|
+
const options = getDefaultNetworkOptions(themeColors, this.settings);
|
|
29776
30063
|
this.network = new Network(
|
|
29777
30064
|
container,
|
|
29778
30065
|
{ nodes: this.nodesDataSet, edges: this.edgesDataSet },
|
|
29779
30066
|
options
|
|
29780
30067
|
);
|
|
30068
|
+
this.setupHtmlTooltips(container);
|
|
29781
30069
|
this.applyCanvasBackground(themeColors.background);
|
|
29782
|
-
this.
|
|
29783
|
-
|
|
29784
|
-
this.network.
|
|
29785
|
-
|
|
30070
|
+
this.setupContainerResize(container);
|
|
30071
|
+
if (this.settings.physicsEnabled) {
|
|
30072
|
+
this.network.on("stabilizationIterationsDone", () => {
|
|
30073
|
+
this.network.setOptions({ physics: { enabled: true } });
|
|
30074
|
+
this.network.fit({ maxZoomLevel: 3 });
|
|
30075
|
+
});
|
|
30076
|
+
} else {
|
|
30077
|
+
setTimeout(() => {
|
|
30078
|
+
if (this.network) {
|
|
30079
|
+
this.network.fit({ maxZoomLevel: 3 });
|
|
30080
|
+
}
|
|
30081
|
+
}, 100);
|
|
30082
|
+
}
|
|
30083
|
+
this.network.on("dragEnd", (params) => {
|
|
30084
|
+
if (params.nodes.length > 0) {
|
|
30085
|
+
const positions = this.network.getPositions(params.nodes);
|
|
30086
|
+
const updates = params.nodes.map((id2) => ({
|
|
30087
|
+
id: id2,
|
|
30088
|
+
x: positions[id2].x,
|
|
30089
|
+
y: positions[id2].y,
|
|
30090
|
+
fixed: { x: true, y: true }
|
|
30091
|
+
}));
|
|
30092
|
+
this.nodesDataSet.update(updates);
|
|
30093
|
+
}
|
|
29786
30094
|
});
|
|
29787
30095
|
if (!this.themeObserver) {
|
|
29788
30096
|
this.themeObserver = watchThemeChanges((newTheme) => {
|
|
@@ -29801,6 +30109,20 @@ var GraphPlugin = class {
|
|
|
29801
30109
|
}
|
|
29802
30110
|
};
|
|
29803
30111
|
controls.appendChild(fitButton);
|
|
30112
|
+
const settingsButton = document.createElement("button");
|
|
30113
|
+
settingsButton.className = "yasgui-graph-button yasgui-graph-settings-button";
|
|
30114
|
+
settingsButton.setAttribute("aria-label", "Graph settings");
|
|
30115
|
+
settingsButton.innerHTML = `<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
30116
|
+
<path d="M8 10.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z"/>
|
|
30117
|
+
<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"/>
|
|
30118
|
+
</svg> Settings`;
|
|
30119
|
+
settingsButton.onclick = () => {
|
|
30120
|
+
this.toggleSettingsPanel(container);
|
|
30121
|
+
};
|
|
30122
|
+
controls.appendChild(settingsButton);
|
|
30123
|
+
if (wasPanelOpen) {
|
|
30124
|
+
this.toggleSettingsPanel(container);
|
|
30125
|
+
}
|
|
29804
30126
|
} catch (error) {
|
|
29805
30127
|
console.error("Error rendering graph:", error);
|
|
29806
30128
|
const errorDiv = document.createElement("div");
|
|
@@ -29809,6 +30131,111 @@ var GraphPlugin = class {
|
|
|
29809
30131
|
this.yasr.resultsEl.appendChild(errorDiv);
|
|
29810
30132
|
}
|
|
29811
30133
|
}
|
|
30134
|
+
/**
|
|
30135
|
+
* Setup custom HTML tooltip rendering for vis-network
|
|
30136
|
+
* @param container - The graph container element
|
|
30137
|
+
*/
|
|
30138
|
+
setupHtmlTooltips(container) {
|
|
30139
|
+
if (!this.network) return;
|
|
30140
|
+
let hideTimeout = null;
|
|
30141
|
+
this.network.on("hoverNode", (params) => {
|
|
30142
|
+
if (hideTimeout) {
|
|
30143
|
+
clearTimeout(hideTimeout);
|
|
30144
|
+
hideTimeout = null;
|
|
30145
|
+
}
|
|
30146
|
+
const nodeId = params.node;
|
|
30147
|
+
const node = this.nodesDataSet.get(nodeId);
|
|
30148
|
+
if (node && node.title) {
|
|
30149
|
+
this.showHtmlTooltip(container, node.title, params.pointer.DOM);
|
|
30150
|
+
}
|
|
30151
|
+
});
|
|
30152
|
+
this.network.on("hoverEdge", (params) => {
|
|
30153
|
+
if (hideTimeout) {
|
|
30154
|
+
clearTimeout(hideTimeout);
|
|
30155
|
+
hideTimeout = null;
|
|
30156
|
+
}
|
|
30157
|
+
const edgeId = params.edge;
|
|
30158
|
+
const edge = this.edgesDataSet.get(edgeId);
|
|
30159
|
+
if (edge && edge.title) {
|
|
30160
|
+
this.showHtmlTooltip(container, edge.title, params.pointer.DOM);
|
|
30161
|
+
}
|
|
30162
|
+
});
|
|
30163
|
+
this.network.on("blurNode", () => {
|
|
30164
|
+
hideTimeout = window.setTimeout(() => {
|
|
30165
|
+
this.hideHtmlTooltipIfNotHovered(container);
|
|
30166
|
+
}, 200);
|
|
30167
|
+
});
|
|
30168
|
+
this.network.on("blurEdge", () => {
|
|
30169
|
+
hideTimeout = window.setTimeout(() => {
|
|
30170
|
+
this.hideHtmlTooltipIfNotHovered(container);
|
|
30171
|
+
}, 200);
|
|
30172
|
+
});
|
|
30173
|
+
this.network.on("dragStart", () => {
|
|
30174
|
+
if (hideTimeout) {
|
|
30175
|
+
clearTimeout(hideTimeout);
|
|
30176
|
+
hideTimeout = null;
|
|
30177
|
+
}
|
|
30178
|
+
this.hideHtmlTooltip(container);
|
|
30179
|
+
});
|
|
30180
|
+
this.network.on("zoom", () => {
|
|
30181
|
+
if (hideTimeout) {
|
|
30182
|
+
clearTimeout(hideTimeout);
|
|
30183
|
+
hideTimeout = null;
|
|
30184
|
+
}
|
|
30185
|
+
this.hideHtmlTooltip(container);
|
|
30186
|
+
});
|
|
30187
|
+
}
|
|
30188
|
+
/**
|
|
30189
|
+
* Show HTML tooltip at specified position
|
|
30190
|
+
* @param container - Container element
|
|
30191
|
+
* @param htmlContent - HTML content to display
|
|
30192
|
+
* @param position - Mouse position {x, y}
|
|
30193
|
+
*/
|
|
30194
|
+
showHtmlTooltip(container, htmlContent, position) {
|
|
30195
|
+
this.hideHtmlTooltip(container);
|
|
30196
|
+
const tooltip = document.createElement("div");
|
|
30197
|
+
tooltip.className = "yasgui-graph-tooltip-container";
|
|
30198
|
+
tooltip.innerHTML = htmlContent;
|
|
30199
|
+
tooltip.style.position = "absolute";
|
|
30200
|
+
tooltip.style.left = `${position.x + 10}px`;
|
|
30201
|
+
tooltip.style.top = `${position.y + 10}px`;
|
|
30202
|
+
tooltip.style.zIndex = "1000";
|
|
30203
|
+
tooltip.addEventListener("mouseleave", () => {
|
|
30204
|
+
this.hideHtmlTooltip(container);
|
|
30205
|
+
});
|
|
30206
|
+
container.appendChild(tooltip);
|
|
30207
|
+
const rect = tooltip.getBoundingClientRect();
|
|
30208
|
+
const containerRect = container.getBoundingClientRect();
|
|
30209
|
+
if (rect.right > containerRect.right) {
|
|
30210
|
+
tooltip.style.left = `${position.x - rect.width - 10}px`;
|
|
30211
|
+
}
|
|
30212
|
+
if (rect.bottom > containerRect.bottom) {
|
|
30213
|
+
tooltip.style.top = `${position.y - rect.height - 10}px`;
|
|
30214
|
+
}
|
|
30215
|
+
}
|
|
30216
|
+
/**
|
|
30217
|
+
* Hide HTML tooltip
|
|
30218
|
+
* @param container - Container element
|
|
30219
|
+
*/
|
|
30220
|
+
hideHtmlTooltip(container) {
|
|
30221
|
+
const existingTooltip = container.querySelector(".yasgui-graph-tooltip-container");
|
|
30222
|
+
if (existingTooltip) {
|
|
30223
|
+
existingTooltip.remove();
|
|
30224
|
+
}
|
|
30225
|
+
}
|
|
30226
|
+
/**
|
|
30227
|
+
* Hide HTML tooltip only if mouse is not hovering over it
|
|
30228
|
+
* @param container - Container element
|
|
30229
|
+
*/
|
|
30230
|
+
hideHtmlTooltipIfNotHovered(container) {
|
|
30231
|
+
const existingTooltip = container.querySelector(".yasgui-graph-tooltip-container");
|
|
30232
|
+
if (existingTooltip) {
|
|
30233
|
+
const isHovered = existingTooltip.matches(":hover");
|
|
30234
|
+
if (!isHovered) {
|
|
30235
|
+
existingTooltip.remove();
|
|
30236
|
+
}
|
|
30237
|
+
}
|
|
30238
|
+
}
|
|
29812
30239
|
/**
|
|
29813
30240
|
* Apply theme to existing network
|
|
29814
30241
|
* @param newTheme - 'light' or 'dark'
|
|
@@ -29819,12 +30246,30 @@ var GraphPlugin = class {
|
|
|
29819
30246
|
}
|
|
29820
30247
|
this.currentTheme = newTheme;
|
|
29821
30248
|
const themeColors = getThemeNodeColors(newTheme);
|
|
29822
|
-
const
|
|
30249
|
+
const fixedNodes = {};
|
|
30250
|
+
this.nodesDataSet.get().forEach((node) => {
|
|
30251
|
+
var _a, _b;
|
|
30252
|
+
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);
|
|
30253
|
+
if (isFixed && node.x !== void 0 && node.y !== void 0) {
|
|
30254
|
+
fixedNodes[node.id] = { x: node.x, y: node.y };
|
|
30255
|
+
}
|
|
30256
|
+
});
|
|
30257
|
+
const { nodes, edges } = triplesToGraph(this.triples, this.prefixMap, themeColors, this.settings);
|
|
29823
30258
|
this.nodesDataSet.clear();
|
|
29824
30259
|
this.nodesDataSet.add(nodes);
|
|
29825
30260
|
this.edgesDataSet.clear();
|
|
29826
30261
|
this.edgesDataSet.add(edges);
|
|
29827
|
-
const
|
|
30262
|
+
const fixedIds = Object.keys(fixedNodes);
|
|
30263
|
+
if (fixedIds.length > 0) {
|
|
30264
|
+
const updates = fixedIds.map((id2) => ({
|
|
30265
|
+
id: id2,
|
|
30266
|
+
x: fixedNodes[id2].x,
|
|
30267
|
+
y: fixedNodes[id2].y,
|
|
30268
|
+
fixed: { x: true, y: true }
|
|
30269
|
+
}));
|
|
30270
|
+
this.nodesDataSet.update(updates);
|
|
30271
|
+
}
|
|
30272
|
+
const options = getDefaultNetworkOptions(themeColors, this.settings);
|
|
29828
30273
|
this.network.setOptions(options);
|
|
29829
30274
|
this.applyCanvasBackground(themeColors.background);
|
|
29830
30275
|
}
|
|
@@ -29860,6 +30305,174 @@ var GraphPlugin = class {
|
|
|
29860
30305
|
});
|
|
29861
30306
|
this.resizeObserver.observe(parent2);
|
|
29862
30307
|
}
|
|
30308
|
+
/**
|
|
30309
|
+
* Toggle the settings panel open/closed
|
|
30310
|
+
* @param container - The graph container element
|
|
30311
|
+
*/
|
|
30312
|
+
toggleSettingsPanel(container) {
|
|
30313
|
+
const existing = container.querySelector(".yasgui-graph-settings-panel");
|
|
30314
|
+
if (existing) {
|
|
30315
|
+
existing.remove();
|
|
30316
|
+
this.settingsPanelOpen = false;
|
|
30317
|
+
this.removeClickOutsideHandler();
|
|
30318
|
+
} else {
|
|
30319
|
+
const panel = this.createSettingsPanel(container);
|
|
30320
|
+
container.appendChild(panel);
|
|
30321
|
+
this.settingsPanelOpen = true;
|
|
30322
|
+
this.setupClickOutsideHandler(container, panel);
|
|
30323
|
+
}
|
|
30324
|
+
}
|
|
30325
|
+
/**
|
|
30326
|
+
* Setup click-outside-to-close handler for settings panel
|
|
30327
|
+
* @param container - The graph container element
|
|
30328
|
+
* @param panel - The settings panel element
|
|
30329
|
+
*/
|
|
30330
|
+
setupClickOutsideHandler(container, panel) {
|
|
30331
|
+
this.removeClickOutsideHandler();
|
|
30332
|
+
this.clickOutsideHandler = (event) => {
|
|
30333
|
+
const target = event.target;
|
|
30334
|
+
if (!panel.contains(target) && !this.isSettingsButton(target)) {
|
|
30335
|
+
this.toggleSettingsPanel(container);
|
|
30336
|
+
}
|
|
30337
|
+
};
|
|
30338
|
+
setTimeout(() => {
|
|
30339
|
+
document.addEventListener("click", this.clickOutsideHandler);
|
|
30340
|
+
}, 100);
|
|
30341
|
+
}
|
|
30342
|
+
/**
|
|
30343
|
+
* Remove the click-outside handler
|
|
30344
|
+
*/
|
|
30345
|
+
removeClickOutsideHandler() {
|
|
30346
|
+
if (this.clickOutsideHandler) {
|
|
30347
|
+
document.removeEventListener("click", this.clickOutsideHandler);
|
|
30348
|
+
this.clickOutsideHandler = null;
|
|
30349
|
+
}
|
|
30350
|
+
}
|
|
30351
|
+
/**
|
|
30352
|
+
* Check if a node is the settings button or inside it
|
|
30353
|
+
* @param node - The node to check
|
|
30354
|
+
*/
|
|
30355
|
+
isSettingsButton(node) {
|
|
30356
|
+
let current = node;
|
|
30357
|
+
while (current) {
|
|
30358
|
+
if (current instanceof Element && current.classList.contains("yasgui-graph-settings-button")) {
|
|
30359
|
+
return true;
|
|
30360
|
+
}
|
|
30361
|
+
current = current.parentNode;
|
|
30362
|
+
}
|
|
30363
|
+
return false;
|
|
30364
|
+
}
|
|
30365
|
+
/**
|
|
30366
|
+
* Build and return the settings panel element
|
|
30367
|
+
* @param container - The graph container element (used to re-draw on change)
|
|
30368
|
+
*/
|
|
30369
|
+
createSettingsPanel(_container) {
|
|
30370
|
+
const panel = document.createElement("div");
|
|
30371
|
+
panel.className = "yasgui-graph-settings-panel";
|
|
30372
|
+
panel.setAttribute("role", "dialog");
|
|
30373
|
+
panel.setAttribute("aria-label", "Graph settings");
|
|
30374
|
+
const title = document.createElement("div");
|
|
30375
|
+
title.className = "yasgui-graph-settings-title";
|
|
30376
|
+
title.textContent = "Graph Settings";
|
|
30377
|
+
panel.appendChild(title);
|
|
30378
|
+
const addSection = (label) => {
|
|
30379
|
+
const h = document.createElement("div");
|
|
30380
|
+
h.className = "yasgui-graph-settings-section";
|
|
30381
|
+
h.textContent = label;
|
|
30382
|
+
panel.appendChild(h);
|
|
30383
|
+
};
|
|
30384
|
+
const addToggle = (label, checked, onChange) => {
|
|
30385
|
+
const row = document.createElement("label");
|
|
30386
|
+
row.className = "yasgui-graph-settings-row";
|
|
30387
|
+
const input = document.createElement("input");
|
|
30388
|
+
input.type = "checkbox";
|
|
30389
|
+
input.checked = checked;
|
|
30390
|
+
input.addEventListener("change", () => onChange(input.checked));
|
|
30391
|
+
const span = document.createElement("span");
|
|
30392
|
+
span.textContent = label;
|
|
30393
|
+
row.appendChild(input);
|
|
30394
|
+
row.appendChild(span);
|
|
30395
|
+
panel.appendChild(row);
|
|
30396
|
+
};
|
|
30397
|
+
const addSelect = (label, options, current, onChange) => {
|
|
30398
|
+
const row = document.createElement("div");
|
|
30399
|
+
row.className = "yasgui-graph-settings-row";
|
|
30400
|
+
const lbl = document.createElement("span");
|
|
30401
|
+
lbl.textContent = label;
|
|
30402
|
+
const sel = document.createElement("select");
|
|
30403
|
+
sel.className = "yasgui-graph-settings-select";
|
|
30404
|
+
options.forEach((o) => {
|
|
30405
|
+
const opt = document.createElement("option");
|
|
30406
|
+
opt.value = o.value;
|
|
30407
|
+
opt.textContent = o.label;
|
|
30408
|
+
if (o.value === current) opt.selected = true;
|
|
30409
|
+
sel.appendChild(opt);
|
|
30410
|
+
});
|
|
30411
|
+
sel.addEventListener("change", () => onChange(sel.value));
|
|
30412
|
+
row.appendChild(lbl);
|
|
30413
|
+
row.appendChild(sel);
|
|
30414
|
+
panel.appendChild(row);
|
|
30415
|
+
};
|
|
30416
|
+
const applyAndRedraw = () => {
|
|
30417
|
+
saveSettings(this.settings);
|
|
30418
|
+
this.draw();
|
|
30419
|
+
};
|
|
30420
|
+
addSection("Arrows");
|
|
30421
|
+
addSelect(
|
|
30422
|
+
"Style",
|
|
30423
|
+
[
|
|
30424
|
+
{ value: "curved", label: "Curved" },
|
|
30425
|
+
{ value: "straight", label: "Straight" }
|
|
30426
|
+
],
|
|
30427
|
+
this.settings.edgeStyle,
|
|
30428
|
+
(v) => {
|
|
30429
|
+
this.settings.edgeStyle = v;
|
|
30430
|
+
applyAndRedraw();
|
|
30431
|
+
}
|
|
30432
|
+
);
|
|
30433
|
+
addSection("Predicate display");
|
|
30434
|
+
addSelect(
|
|
30435
|
+
"Display",
|
|
30436
|
+
[
|
|
30437
|
+
{ value: "label", label: "Label (prefixed URI)" },
|
|
30438
|
+
{ value: "icon", label: "Icon / symbol" },
|
|
30439
|
+
{ value: "none", label: "Hidden" }
|
|
30440
|
+
],
|
|
30441
|
+
this.settings.predicateDisplay,
|
|
30442
|
+
(v) => {
|
|
30443
|
+
this.settings.predicateDisplay = v;
|
|
30444
|
+
applyAndRedraw();
|
|
30445
|
+
}
|
|
30446
|
+
);
|
|
30447
|
+
addSection("Compact mode");
|
|
30448
|
+
addToggle("Compact mode", this.settings.compactMode, (v) => {
|
|
30449
|
+
this.settings.compactMode = v;
|
|
30450
|
+
applyAndRedraw();
|
|
30451
|
+
});
|
|
30452
|
+
addSection("Display");
|
|
30453
|
+
addToggle("Show node labels", this.settings.showNodeLabels, (v) => {
|
|
30454
|
+
this.settings.showNodeLabels = v;
|
|
30455
|
+
applyAndRedraw();
|
|
30456
|
+
});
|
|
30457
|
+
addToggle("Enable physics", this.settings.physicsEnabled, (v) => {
|
|
30458
|
+
this.settings.physicsEnabled = v;
|
|
30459
|
+
applyAndRedraw();
|
|
30460
|
+
});
|
|
30461
|
+
addSelect(
|
|
30462
|
+
"Node size",
|
|
30463
|
+
[
|
|
30464
|
+
{ value: "small", label: "Small" },
|
|
30465
|
+
{ value: "medium", label: "Medium" },
|
|
30466
|
+
{ value: "large", label: "Large" }
|
|
30467
|
+
],
|
|
30468
|
+
this.settings.nodeSize,
|
|
30469
|
+
(v) => {
|
|
30470
|
+
this.settings.nodeSize = v;
|
|
30471
|
+
applyAndRedraw();
|
|
30472
|
+
}
|
|
30473
|
+
);
|
|
30474
|
+
return panel;
|
|
30475
|
+
}
|
|
29863
30476
|
/**
|
|
29864
30477
|
* Get icon for plugin tab
|
|
29865
30478
|
* @returns Icon element
|
|
@@ -29882,6 +30495,7 @@ var GraphPlugin = class {
|
|
|
29882
30495
|
* Cleanup when plugin is destroyed
|
|
29883
30496
|
*/
|
|
29884
30497
|
destroy() {
|
|
30498
|
+
this.removeClickOutsideHandler();
|
|
29885
30499
|
if (this.themeObserver) {
|
|
29886
30500
|
this.themeObserver.disconnect();
|
|
29887
30501
|
this.themeObserver = null;
|