@matdata/yasgui-graph-plugin 1.0.8 → 1.1.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 +6 -6
- package/dist/yasgui-graph-plugin.cjs.js +147 -42
- package/dist/yasgui-graph-plugin.cjs.js.map +3 -3
- package/dist/yasgui-graph-plugin.esm.js +147 -42
- package/dist/yasgui-graph-plugin.esm.js.map +3 -3
- package/dist/yasgui-graph-plugin.min.js +32 -25
- package/dist/yasgui-graph-plugin.min.js.map +4 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -24,11 +24,11 @@ A YASGUI plugin for visualizing SPARQL CONSTRUCT and DESCRIBE query results as i
|
|
|
24
24
|
### NPM
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
npm install @matdata/yasgui-graph-plugin @
|
|
27
|
+
npm install @matdata/yasgui-graph-plugin @matdata/yasgui vis-network
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
```javascript
|
|
31
|
-
import Yasgui from '@
|
|
31
|
+
import Yasgui from '@matdata/yasgui';
|
|
32
32
|
import GraphPlugin from '@matdata/yasgui-graph-plugin';
|
|
33
33
|
|
|
34
34
|
Yasgui.Yasr.registerPlugin('Graph', GraphPlugin);
|
|
@@ -40,8 +40,8 @@ const yasgui = new Yasgui(document.getElementById('yasgui'));
|
|
|
40
40
|
|
|
41
41
|
```html
|
|
42
42
|
<!-- YASGUI -->
|
|
43
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@
|
|
44
|
-
<script src="https://cdn.jsdelivr.net/npm/@
|
|
43
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@matdata/yasgui@5.0.0/build/yasgui.min.css">
|
|
44
|
+
<script src="https://cdn.jsdelivr.net/npm/@matdata/yasgui@5.0.0/build/yasgui.min.js"></script>
|
|
45
45
|
|
|
46
46
|
<!-- Graph Plugin -->
|
|
47
47
|
<script src="https://cdn.jsdelivr.net/npm/@matdata/yasgui-graph-plugin/dist/yasgui-graph-plugin.min.js"></script>
|
|
@@ -197,8 +197,8 @@ Contributions welcome! Please follow the project constitution (`.specify/memory/
|
|
|
197
197
|
## 🙏 Acknowledgments
|
|
198
198
|
|
|
199
199
|
- Built with [vis-network](https://visjs.github.io/vis-network/) for graph rendering
|
|
200
|
-
- Integrates with [YASGUI](https://github.com/
|
|
201
|
-
- Follows the [yasgui-geo](https://github.com/
|
|
200
|
+
- Integrates with [YASGUI](https://github.com/matdata/yasgui) SPARQL editor
|
|
201
|
+
- Follows the [yasgui-geo](https://github.com/matdata/yasgui-geo) plugin pattern
|
|
202
202
|
|
|
203
203
|
## 📊 Project Status
|
|
204
204
|
|
|
@@ -69,7 +69,7 @@ function truncateLabel(text, maxLength = 50) {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
// src/networkConfig.js
|
|
72
|
-
function getDefaultNetworkOptions() {
|
|
72
|
+
function getDefaultNetworkOptions(themeColors) {
|
|
73
73
|
return {
|
|
74
74
|
autoResize: true,
|
|
75
75
|
width: "100%",
|
|
@@ -100,27 +100,20 @@ function getDefaultNetworkOptions() {
|
|
|
100
100
|
},
|
|
101
101
|
nodes: {
|
|
102
102
|
shape: "dot",
|
|
103
|
-
size:
|
|
103
|
+
size: 10,
|
|
104
104
|
font: {
|
|
105
|
-
size:
|
|
106
|
-
color:
|
|
107
|
-
align: "center",
|
|
108
|
-
vadjust: 0,
|
|
109
|
-
multi: false
|
|
105
|
+
size: 12,
|
|
106
|
+
color: themeColors.text
|
|
110
107
|
},
|
|
111
|
-
borderWidth:
|
|
112
|
-
borderWidthSelected:
|
|
113
|
-
labelHighlightBold: true
|
|
114
|
-
fixed: {
|
|
115
|
-
x: false,
|
|
116
|
-
y: false
|
|
117
|
-
}
|
|
108
|
+
borderWidth: 1,
|
|
109
|
+
borderWidthSelected: 2,
|
|
110
|
+
labelHighlightBold: true
|
|
118
111
|
},
|
|
119
112
|
edges: {
|
|
120
113
|
arrows: {
|
|
121
114
|
to: {
|
|
122
115
|
enabled: true,
|
|
123
|
-
scaleFactor: 0.
|
|
116
|
+
scaleFactor: 0.6
|
|
124
117
|
}
|
|
125
118
|
},
|
|
126
119
|
smooth: {
|
|
@@ -129,7 +122,14 @@ function getDefaultNetworkOptions() {
|
|
|
129
122
|
},
|
|
130
123
|
font: {
|
|
131
124
|
size: 12,
|
|
132
|
-
align: "middle"
|
|
125
|
+
align: "middle",
|
|
126
|
+
color: themeColors.edgeLabel,
|
|
127
|
+
background: themeColors.edgeLabelBackground,
|
|
128
|
+
strokeWidth: 0
|
|
129
|
+
// Remove white outline/halo
|
|
130
|
+
},
|
|
131
|
+
color: {
|
|
132
|
+
color: themeColors.edge
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
};
|
|
@@ -162,24 +162,24 @@ function parseConstructResults(yasrResults) {
|
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
// src/colorUtils.js
|
|
165
|
-
function getNodeColor(node, triples) {
|
|
165
|
+
function getNodeColor(node, triples, themeColors) {
|
|
166
166
|
if (node.uri && node.uri.startsWith("_:")) {
|
|
167
|
-
return
|
|
167
|
+
return themeColors.blankNode;
|
|
168
168
|
}
|
|
169
169
|
if (node.type === "literal") {
|
|
170
|
-
return
|
|
170
|
+
return themeColors.literal;
|
|
171
171
|
}
|
|
172
172
|
const isTypeObject = triples.some(
|
|
173
173
|
(triple) => triple.predicate === "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" && triple.object.value === node.uri
|
|
174
174
|
);
|
|
175
175
|
if (isTypeObject) {
|
|
176
|
-
return
|
|
176
|
+
return themeColors.typeObject;
|
|
177
177
|
}
|
|
178
|
-
return
|
|
178
|
+
return themeColors.uri;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
// src/transformers.js
|
|
182
|
-
function createNodeMap(triples, prefixMap) {
|
|
182
|
+
function createNodeMap(triples, prefixMap, themeColors) {
|
|
183
183
|
const nodeMap = /* @__PURE__ */ new Map();
|
|
184
184
|
let nodeId = 1;
|
|
185
185
|
triples.forEach((triple) => {
|
|
@@ -190,15 +190,10 @@ function createNodeMap(triples, prefixMap) {
|
|
|
190
190
|
id: nodeId++,
|
|
191
191
|
uri: triple.subject,
|
|
192
192
|
label,
|
|
193
|
-
color: getNodeColor({ uri: triple.subject, type: "uri" }, triples),
|
|
193
|
+
color: getNodeColor({ uri: triple.subject, type: "uri" }, triples, themeColors),
|
|
194
194
|
type: "uri",
|
|
195
195
|
fullValue: triple.subject,
|
|
196
|
-
title: isBlankNode ? triple.subject : applyPrefix(triple.subject, prefixMap)
|
|
197
|
-
font: {
|
|
198
|
-
vadjust: -30,
|
|
199
|
-
// Position label above the node
|
|
200
|
-
align: "center"
|
|
201
|
-
}
|
|
196
|
+
title: isBlankNode ? triple.subject : applyPrefix(triple.subject, prefixMap)
|
|
202
197
|
});
|
|
203
198
|
}
|
|
204
199
|
const objValue = triple.object.value;
|
|
@@ -225,16 +220,12 @@ function createNodeMap(triples, prefixMap) {
|
|
|
225
220
|
label,
|
|
226
221
|
color: getNodeColor(
|
|
227
222
|
{ uri: objValue, type: isLiteral ? "literal" : "uri" },
|
|
228
|
-
triples
|
|
223
|
+
triples,
|
|
224
|
+
themeColors
|
|
229
225
|
),
|
|
230
226
|
type: isLiteral ? "literal" : "uri",
|
|
231
227
|
fullValue,
|
|
232
|
-
title
|
|
233
|
-
font: {
|
|
234
|
-
vadjust: -30,
|
|
235
|
-
// Position label above the node
|
|
236
|
-
align: "center"
|
|
237
|
-
}
|
|
228
|
+
title
|
|
238
229
|
});
|
|
239
230
|
}
|
|
240
231
|
});
|
|
@@ -264,8 +255,8 @@ function createEdgesArray(triples, nodeMap, prefixMap) {
|
|
|
264
255
|
});
|
|
265
256
|
return edges;
|
|
266
257
|
}
|
|
267
|
-
function triplesToGraph(triples, prefixMap) {
|
|
268
|
-
const nodeMap = createNodeMap(triples, prefixMap);
|
|
258
|
+
function triplesToGraph(triples, prefixMap, themeColors) {
|
|
259
|
+
const nodeMap = createNodeMap(triples, prefixMap, themeColors);
|
|
269
260
|
const edges = createEdgesArray(triples, nodeMap, prefixMap);
|
|
270
261
|
const nodes = Array.from(nodeMap.values());
|
|
271
262
|
return { nodes, edges };
|
|
@@ -29972,6 +29963,66 @@ Network.prototype.getOptionsFromConfigurator = function() {
|
|
|
29972
29963
|
return options;
|
|
29973
29964
|
};
|
|
29974
29965
|
|
|
29966
|
+
// src/themeUtils.js
|
|
29967
|
+
function getCurrentTheme() {
|
|
29968
|
+
return document.documentElement.getAttribute("data-theme") || "light";
|
|
29969
|
+
}
|
|
29970
|
+
function getThemeNodeColors(theme) {
|
|
29971
|
+
if (theme === "dark") {
|
|
29972
|
+
return {
|
|
29973
|
+
uri: "#97C2FC",
|
|
29974
|
+
// Light blue for URIs
|
|
29975
|
+
literal: "#a6c8a6ff",
|
|
29976
|
+
// Light green for literals
|
|
29977
|
+
blankNode: "#888888",
|
|
29978
|
+
// Medium grey for blank nodes (darker than light mode)
|
|
29979
|
+
typeObject: "#e15b13ff",
|
|
29980
|
+
// Orange for rdf:type objects
|
|
29981
|
+
text: "#e0e0e0",
|
|
29982
|
+
// Light text for dark backgrounds
|
|
29983
|
+
edge: "#666666",
|
|
29984
|
+
// Darker edges
|
|
29985
|
+
edgeLabel: "#cccccc",
|
|
29986
|
+
// Lighter edge labels
|
|
29987
|
+
edgeLabelBackground: "rgba(30, 30, 30, 0.8)"
|
|
29988
|
+
// Dark semi-transparent background
|
|
29989
|
+
};
|
|
29990
|
+
}
|
|
29991
|
+
return {
|
|
29992
|
+
uri: "#97C2FC",
|
|
29993
|
+
// Light blue for URIs
|
|
29994
|
+
literal: "#a6c8a6ff",
|
|
29995
|
+
// Light green for literals
|
|
29996
|
+
blankNode: "#c5c5c5ff",
|
|
29997
|
+
// Light grey for blank nodes
|
|
29998
|
+
typeObject: "#e15b13ff",
|
|
29999
|
+
// Orange for rdf:type objects
|
|
30000
|
+
text: "#000000",
|
|
30001
|
+
// Black text
|
|
30002
|
+
edge: "#cccccc",
|
|
30003
|
+
// Light grey edges
|
|
30004
|
+
edgeLabel: "#666666",
|
|
30005
|
+
// Dark grey edge labels
|
|
30006
|
+
edgeLabelBackground: "rgba(255, 255, 255, 0.8)"
|
|
30007
|
+
// Light semi-transparent background
|
|
30008
|
+
};
|
|
30009
|
+
}
|
|
30010
|
+
function watchThemeChanges(callback) {
|
|
30011
|
+
const observer = new MutationObserver((mutations) => {
|
|
30012
|
+
mutations.forEach((mutation) => {
|
|
30013
|
+
if (mutation.attributeName === "data-theme") {
|
|
30014
|
+
const theme = getCurrentTheme();
|
|
30015
|
+
callback(theme);
|
|
30016
|
+
}
|
|
30017
|
+
});
|
|
30018
|
+
});
|
|
30019
|
+
observer.observe(document.documentElement, {
|
|
30020
|
+
attributes: true,
|
|
30021
|
+
attributeFilter: ["data-theme"]
|
|
30022
|
+
});
|
|
30023
|
+
return observer;
|
|
30024
|
+
}
|
|
30025
|
+
|
|
29975
30026
|
// src/GraphPlugin.js
|
|
29976
30027
|
function getVisNetwork() {
|
|
29977
30028
|
return { Network, DataSet };
|
|
@@ -29980,6 +30031,8 @@ var GraphPlugin = class {
|
|
|
29980
30031
|
constructor(yasr) {
|
|
29981
30032
|
this.yasr = yasr;
|
|
29982
30033
|
this.network = null;
|
|
30034
|
+
this.currentTheme = getCurrentTheme();
|
|
30035
|
+
this.themeObserver = null;
|
|
29983
30036
|
}
|
|
29984
30037
|
/**
|
|
29985
30038
|
* Plugin priority (higher = shown first in tabs)
|
|
@@ -30022,7 +30075,9 @@ var GraphPlugin = class {
|
|
|
30022
30075
|
return;
|
|
30023
30076
|
}
|
|
30024
30077
|
const prefixMap = extractPrefixes(this.yasr);
|
|
30025
|
-
|
|
30078
|
+
this.currentTheme = getCurrentTheme();
|
|
30079
|
+
const themeColors = getThemeNodeColors(this.currentTheme);
|
|
30080
|
+
const { nodes, edges } = triplesToGraph(triples, prefixMap, themeColors);
|
|
30026
30081
|
const container = document.createElement("div");
|
|
30027
30082
|
container.style.width = "100%";
|
|
30028
30083
|
container.style.height = "600px";
|
|
@@ -30032,7 +30087,11 @@ var GraphPlugin = class {
|
|
|
30032
30087
|
const { Network: Network2, DataSet: DataSet2 } = getVisNetwork();
|
|
30033
30088
|
const nodesDataSet = new DataSet2(nodes);
|
|
30034
30089
|
const edgesDataSet = new DataSet2(edges);
|
|
30035
|
-
const options = getDefaultNetworkOptions();
|
|
30090
|
+
const options = getDefaultNetworkOptions(themeColors);
|
|
30091
|
+
this.nodesDataSet = nodesDataSet;
|
|
30092
|
+
this.edgesDataSet = edgesDataSet;
|
|
30093
|
+
this.triples = triples;
|
|
30094
|
+
this.prefixMap = prefixMap;
|
|
30036
30095
|
this.network = new Network2(
|
|
30037
30096
|
container,
|
|
30038
30097
|
{ nodes: nodesDataSet, edges: edgesDataSet },
|
|
@@ -30043,6 +30102,11 @@ var GraphPlugin = class {
|
|
|
30043
30102
|
this.network.setOptions({ physics: { enabled: true } });
|
|
30044
30103
|
this.networkReady = true;
|
|
30045
30104
|
});
|
|
30105
|
+
if (!this.themeObserver) {
|
|
30106
|
+
this.themeObserver = watchThemeChanges((newTheme) => {
|
|
30107
|
+
this.applyTheme(newTheme);
|
|
30108
|
+
});
|
|
30109
|
+
}
|
|
30046
30110
|
const controls = document.createElement("div");
|
|
30047
30111
|
controls.style.position = "absolute";
|
|
30048
30112
|
controls.style.top = "10px";
|
|
@@ -30078,16 +30142,57 @@ var GraphPlugin = class {
|
|
|
30078
30142
|
this.yasr.resultsEl.innerHTML = '<div style="padding: 20px; color: red;">Error rendering graph. See console for details.</div>';
|
|
30079
30143
|
}
|
|
30080
30144
|
}
|
|
30145
|
+
/**
|
|
30146
|
+
* Apply theme to existing network
|
|
30147
|
+
* @param {string} newTheme - 'light' or 'dark'
|
|
30148
|
+
*/
|
|
30149
|
+
applyTheme(newTheme) {
|
|
30150
|
+
if (!this.network || !this.nodesDataSet || !this.triples || !this.prefixMap) {
|
|
30151
|
+
return;
|
|
30152
|
+
}
|
|
30153
|
+
this.currentTheme = newTheme;
|
|
30154
|
+
const themeColors = getThemeNodeColors(newTheme);
|
|
30155
|
+
const { nodes, edges } = triplesToGraph(this.triples, this.prefixMap, themeColors);
|
|
30156
|
+
this.nodesDataSet.clear();
|
|
30157
|
+
this.nodesDataSet.add(nodes);
|
|
30158
|
+
this.edgesDataSet.clear();
|
|
30159
|
+
this.edgesDataSet.add(edges);
|
|
30160
|
+
const options = getDefaultNetworkOptions(themeColors);
|
|
30161
|
+
this.network.setOptions(options);
|
|
30162
|
+
}
|
|
30081
30163
|
/**
|
|
30082
30164
|
* Get icon for plugin tab
|
|
30083
30165
|
* @returns {Element} Icon element
|
|
30084
30166
|
*/
|
|
30085
30167
|
getIcon() {
|
|
30086
|
-
const icon = document.createElement("
|
|
30087
|
-
icon.className = "fas fa-project-diagram";
|
|
30168
|
+
const icon = document.createElement("div");
|
|
30088
30169
|
icon.setAttribute("aria-label", "Graph visualization");
|
|
30170
|
+
icon.style.display = "inline-flex";
|
|
30171
|
+
icon.style.alignItems = "center";
|
|
30172
|
+
icon.style.justifyContent = "center";
|
|
30173
|
+
icon.innerHTML = `<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
|
30174
|
+
<circle cx="3" cy="3" r="2" />
|
|
30175
|
+
<circle cx="13" cy="3" r="2" />
|
|
30176
|
+
<circle cx="8" cy="13" r="2" />
|
|
30177
|
+
<line x1="4.5" y1="4" x2="7" y2="11.5" stroke="currentColor" stroke-width="1.5" />
|
|
30178
|
+
<line x1="11.5" y1="4" x2="9" y2="11.5" stroke="currentColor" stroke-width="1.5" />
|
|
30179
|
+
<line x1="5" y1="3" x2="11" y2="3" stroke="currentColor" stroke-width="1.5" />
|
|
30180
|
+
</svg>`;
|
|
30089
30181
|
return icon;
|
|
30090
30182
|
}
|
|
30183
|
+
/**
|
|
30184
|
+
* Cleanup when plugin is destroyed
|
|
30185
|
+
*/
|
|
30186
|
+
destroy() {
|
|
30187
|
+
if (this.themeObserver) {
|
|
30188
|
+
this.themeObserver.disconnect();
|
|
30189
|
+
this.themeObserver = null;
|
|
30190
|
+
}
|
|
30191
|
+
if (this.network) {
|
|
30192
|
+
this.network.destroy();
|
|
30193
|
+
this.network = null;
|
|
30194
|
+
}
|
|
30195
|
+
}
|
|
30091
30196
|
};
|
|
30092
30197
|
var GraphPlugin_default = GraphPlugin;
|
|
30093
30198
|
|