graphwise-reactodia 0.0.1-TR1 → 0.0.1-TR10
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 +115 -1
- package/dist/cjs/graphwise-reactodia.cjs.entry.js +27276 -41135
- package/dist/cjs/graphwise-reactodia.cjs.js +1 -1
- package/dist/cjs/index.cjs.js +7 -0
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/collection/components/graphwise-reactodia/graphwise-reactodia.js +69 -34
- package/dist/collection/components/graphwise-reactodia/i18n/language-key.js +5 -0
- package/dist/collection/components/graphwise-reactodia/i18n/translations.js +3 -1
- package/dist/collection/components/graphwise-reactodia/models/clear-diagram-storage-event.js +5 -0
- package/dist/collection/components/graphwise-reactodia/models/reactodia-config.js +1 -0
- package/dist/collection/components/graphwise-reactodia/models/subscription-list.js +35 -0
- package/dist/collection/components/graphwise-reactodia/models/subscription.js +1 -0
- package/dist/collection/components/graphwise-reactodia/models/triple.js +1 -0
- package/dist/collection/components/graphwise-reactodia/providers/service/service-inject.js +15 -0
- package/dist/collection/components/graphwise-reactodia/providers/service/service.js +19 -0
- package/dist/collection/components/graphwise-reactodia/providers/service/service.provider.js +20 -0
- package/dist/collection/components/graphwise-reactodia/reactodia-app.js +107 -17
- package/dist/collection/components/graphwise-reactodia/services/diagram/diagram.service.js +54 -0
- package/dist/collection/components/graphwise-reactodia/services/diagram-storage/diagram-storage.service.js +55 -0
- package/dist/collection/components/graphwise-reactodia/services/event/event.service.js +35 -0
- package/dist/collection/components/graphwise-reactodia/services/window/window.service.js +24 -0
- package/dist/collection/components/graphwise-reactodia/styles/type-style.resolver.js +53 -0
- package/dist/collection/components/graphwise-reactodia/testing/storage.mock.js +35 -0
- package/dist/collection/index.js +1 -1
- package/dist/collection/pages/graphwise-reactodia/main.js +50 -0
- package/dist/esm/graphwise-reactodia.entry.js +27273 -41132
- package/dist/esm/graphwise-reactodia.js +1 -1
- package/dist/esm/index.js +6 -0
- package/dist/esm/loader.js +1 -1
- package/dist/graphwise-reactodia/graphwise-reactodia.esm.js +1 -1
- package/dist/graphwise-reactodia/index.esm.js +1 -0
- package/dist/graphwise-reactodia/p-b56c58a0.entry.js +1 -0
- package/dist/types/components/graphwise-reactodia/graphwise-reactodia.d.ts +25 -12
- package/dist/types/components/graphwise-reactodia/i18n/language-key.d.ts +4 -0
- package/dist/types/components/graphwise-reactodia/i18n/translations.d.ts +2 -343
- package/dist/types/components/graphwise-reactodia/models/clear-diagram-storage-event.d.ts +5 -0
- package/dist/types/components/graphwise-reactodia/models/reactodia-app-props.d.ts +15 -14
- package/dist/types/components/graphwise-reactodia/models/reactodia-config.d.ts +31 -0
- package/dist/types/components/graphwise-reactodia/models/subscription-list.d.ts +26 -0
- package/dist/types/components/graphwise-reactodia/models/subscription.d.ts +4 -0
- package/dist/types/components/graphwise-reactodia/models/triple.d.ts +19 -0
- package/dist/types/components/graphwise-reactodia/providers/service/service-inject.d.ts +13 -0
- package/dist/types/components/graphwise-reactodia/providers/service/service.d.ts +28 -0
- package/dist/types/components/graphwise-reactodia/providers/service/service.provider.d.ts +16 -0
- package/dist/types/components/graphwise-reactodia/services/diagram/diagram.service.d.ts +26 -0
- package/dist/types/components/graphwise-reactodia/services/diagram-storage/diagram-storage.service.d.ts +26 -0
- package/dist/types/components/graphwise-reactodia/services/event/event.service.d.ts +14 -0
- package/dist/types/components/graphwise-reactodia/services/window/window.service.d.ts +7 -0
- package/dist/types/components/graphwise-reactodia/styles/type-style.resolver.d.ts +11 -0
- package/dist/types/components/graphwise-reactodia/testing/storage.mock.d.ts +18 -0
- package/dist/types/components.d.ts +37 -25
- package/dist/types/index.d.ts +1 -1
- package/package.json +31 -8
- package/dist/graphwise-reactodia/p-bafd6be5.entry.js +0 -1
|
@@ -19,7 +19,7 @@ var patchBrowser = () => {
|
|
|
19
19
|
|
|
20
20
|
patchBrowser().then(async (options) => {
|
|
21
21
|
await appGlobals.globalScripts();
|
|
22
|
-
return index.bootstrapLazy([["graphwise-reactodia.cjs",[[0,"graphwise-reactodia",{"currentRepository":[1,"current-repository"],"
|
|
22
|
+
return index.bootstrapLazy([["graphwise-reactodia.cjs",[[0,"graphwise-reactodia",{"currentRepository":[1,"current-repository"],"config":[16],"language":[1],"providerSettings":[16,"provider-settings"],"theme":[1]},null,{"currentRepository":["onProviderSettingsChange"],"providerSettings":["onProviderSettingsChange"],"language":["onLanguageChange"],"theme":["onThemeChange"]}]]]], options);
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
exports.setNonce = index.setNonce;
|
package/dist/cjs/index.cjs.js
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Event name for clearing the local store state of the diagram. There is an event listener listening for it, which
|
|
5
|
+
* will clear the local store state, whenever this event is fired.
|
|
6
|
+
*/
|
|
7
|
+
const CLEAR_DIAGRAM_STORAGE_EVENT = 'graphwise-reactodia:clear-diagram-storage';
|
|
8
|
+
|
|
9
|
+
exports.CLEAR_DIAGRAM_STORAGE_EVENT = CLEAR_DIAGRAM_STORAGE_EVENT;
|
package/dist/cjs/loader.cjs.js
CHANGED
|
@@ -6,7 +6,7 @@ var appGlobals = require('./app-globals-CcJr5lkF.js');
|
|
|
6
6
|
const defineCustomElements = async (win, options) => {
|
|
7
7
|
if (typeof window === 'undefined') return undefined;
|
|
8
8
|
await appGlobals.globalScripts();
|
|
9
|
-
return index.bootstrapLazy([["graphwise-reactodia.cjs",[[0,"graphwise-reactodia",{"currentRepository":[1,"current-repository"],"
|
|
9
|
+
return index.bootstrapLazy([["graphwise-reactodia.cjs",[[0,"graphwise-reactodia",{"currentRepository":[1,"current-repository"],"config":[16],"language":[1],"providerSettings":[16,"provider-settings"],"theme":[1]},null,{"currentRepository":["onProviderSettingsChange"],"providerSettings":["onProviderSettingsChange"],"language":["onLanguageChange"],"theme":["onThemeChange"]}]]]], options);
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
exports.setNonce = index.setNonce;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { h, Host } from "@stencil/core";
|
|
2
|
-
import {
|
|
2
|
+
import { mountReactodia, unmountReactodia, updateReactodia } from "./reactodia-app";
|
|
3
|
+
import { LanguageKey } from "./i18n/language-key";
|
|
4
|
+
import { resetColors } from "./styles/type-style.resolver";
|
|
3
5
|
/**
|
|
4
6
|
* A web component that renders a graph with the Reactodia workspace.
|
|
5
7
|
*
|
|
6
8
|
* The component is backed by a SPARQL endpoint but is endpoint-agnostic: the host passes
|
|
7
|
-
* the active repository's endpoint via `current-repository` and a `queryFunction`
|
|
8
|
-
* performs the actual HTTP request. Reactodia fetches all node, link and type data
|
|
9
|
-
* through them; the canvas starts empty and the user populates it via the search bar.
|
|
9
|
+
* the active repository's endpoint via `current-repository` and a `config.queryFunction`
|
|
10
|
+
* that performs the actual HTTP request. Reactodia fetches all node, link and type data
|
|
11
|
+
* lazily through them; the canvas starts empty and the user populates it via the search bar.
|
|
10
12
|
*
|
|
11
13
|
* The component is a thin wrapper around Reactodia: query configuration lives outside the
|
|
12
14
|
* wrapper and is supplied through the `providerSettings` prop.
|
|
@@ -16,20 +18,24 @@ export class GraphwiseReactodia {
|
|
|
16
18
|
/**
|
|
17
19
|
* UI language code (e.g. `en`, `fr`) for the Reactodia interface. Defaults to English.
|
|
18
20
|
*/
|
|
19
|
-
this.language =
|
|
21
|
+
this.language = LanguageKey.EN;
|
|
20
22
|
}
|
|
21
23
|
onProviderSettingsChange() {
|
|
22
24
|
this.renderGraph();
|
|
23
25
|
}
|
|
24
26
|
onLanguageChange() {
|
|
25
27
|
// The translations are evaluated by the workspace at construction, so the only way
|
|
26
|
-
// to re-translate the UI is to re-create the graph.
|
|
27
|
-
|
|
28
|
-
const diagram = exportReactodiaLayout();
|
|
29
|
-
this.renderGraph(diagram);
|
|
28
|
+
// to re-translate the UI is to re-create the graph.
|
|
29
|
+
this.renderGraph(true);
|
|
30
30
|
}
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
onThemeChange() {
|
|
32
|
+
resetColors();
|
|
33
|
+
this.renderGraph(true);
|
|
34
|
+
}
|
|
35
|
+
connectedCallback() {
|
|
36
|
+
if (this.config) {
|
|
37
|
+
this.renderGraph();
|
|
38
|
+
}
|
|
33
39
|
}
|
|
34
40
|
disconnectedCallback() {
|
|
35
41
|
if (this.reactRoot) {
|
|
@@ -38,26 +44,27 @@ export class GraphwiseReactodia {
|
|
|
38
44
|
}
|
|
39
45
|
}
|
|
40
46
|
render() {
|
|
41
|
-
return (h(Host, { key: '
|
|
47
|
+
return (h(Host, { key: 'f3247ee3254896e4e4987bd2fa0e3dfce1ce1d2a' }));
|
|
42
48
|
}
|
|
43
|
-
renderGraph(
|
|
49
|
+
renderGraph(isReloading) {
|
|
50
|
+
var _a;
|
|
44
51
|
if (!this.currentRepository) {
|
|
45
52
|
throw new Error('currentRepository is required');
|
|
46
53
|
}
|
|
47
|
-
if (!this.queryFunction) {
|
|
48
|
-
throw new Error('queryFunction is required');
|
|
54
|
+
if (!((_a = this.config) === null || _a === void 0 ? void 0 : _a.queryFunction)) {
|
|
55
|
+
throw new Error('config.queryFunction is required');
|
|
49
56
|
}
|
|
50
57
|
if (!this.hostElement) {
|
|
51
58
|
return;
|
|
52
59
|
}
|
|
53
60
|
const props = {
|
|
54
|
-
|
|
61
|
+
isReload: isReloading,
|
|
55
62
|
currentRepository: this.currentRepository,
|
|
56
|
-
|
|
63
|
+
config: this.config,
|
|
57
64
|
language: this.language,
|
|
58
65
|
providerSettings: this.providerSettings,
|
|
59
66
|
};
|
|
60
|
-
if (this.reactRoot && !
|
|
67
|
+
if (this.reactRoot && !isReloading) {
|
|
61
68
|
void updateReactodia(props);
|
|
62
69
|
}
|
|
63
70
|
else {
|
|
@@ -87,35 +94,35 @@ export class GraphwiseReactodia {
|
|
|
87
94
|
"references": {}
|
|
88
95
|
},
|
|
89
96
|
"required": false,
|
|
90
|
-
"optional":
|
|
97
|
+
"optional": true,
|
|
91
98
|
"docs": {
|
|
92
99
|
"tags": [],
|
|
93
|
-
"text": "The active repository id. Appended to {@link queryFunction} as the request `url`;\nchanging it re-points the graph at the new repository (and resets the canvas),\nwhich is how runtime repository changes are handled."
|
|
100
|
+
"text": "The active repository id. Appended to {@link queryFunction } as the request `url`;\nchanging it re-points the graph at the new repository (and resets the canvas),\nwhich is how runtime repository changes are handled."
|
|
94
101
|
},
|
|
95
102
|
"getter": false,
|
|
96
103
|
"setter": false,
|
|
97
104
|
"reflect": false
|
|
98
105
|
},
|
|
99
|
-
"
|
|
106
|
+
"config": {
|
|
100
107
|
"type": "unknown",
|
|
101
|
-
"attribute": "
|
|
108
|
+
"attribute": "config",
|
|
102
109
|
"mutable": false,
|
|
103
110
|
"complexType": {
|
|
104
|
-
"original": "
|
|
105
|
-
"resolved": "
|
|
111
|
+
"original": "ReactodiaConfig",
|
|
112
|
+
"resolved": "ReactodiaConfig",
|
|
106
113
|
"references": {
|
|
107
|
-
"
|
|
114
|
+
"ReactodiaConfig": {
|
|
108
115
|
"location": "import",
|
|
109
|
-
"path": "
|
|
110
|
-
"id": "
|
|
116
|
+
"path": "./models/reactodia-config",
|
|
117
|
+
"id": "src/components/graphwise-reactodia/models/reactodia-config.ts::ReactodiaConfig"
|
|
111
118
|
}
|
|
112
119
|
}
|
|
113
120
|
},
|
|
114
121
|
"required": false,
|
|
115
|
-
"optional":
|
|
122
|
+
"optional": true,
|
|
116
123
|
"docs": {
|
|
117
124
|
"tags": [],
|
|
118
|
-
"text": "
|
|
125
|
+
"text": "Host-supplied configuration: the SPARQL `queryFunction` transport and an optional `seed`\nset of entities to pre-populate the canvas with. A DOM property (an object, not an\nattribute) passed in from outside the wrapper.\n\nRead once on mount; not watched, as it only sets up the data source and initial canvas."
|
|
119
126
|
},
|
|
120
127
|
"getter": false,
|
|
121
128
|
"setter": false
|
|
@@ -125,9 +132,15 @@ export class GraphwiseReactodia {
|
|
|
125
132
|
"attribute": "language",
|
|
126
133
|
"mutable": false,
|
|
127
134
|
"complexType": {
|
|
128
|
-
"original": "
|
|
129
|
-
"resolved": "
|
|
130
|
-
"references": {
|
|
135
|
+
"original": "LanguageKey",
|
|
136
|
+
"resolved": "LanguageKey.EN | LanguageKey.FR",
|
|
137
|
+
"references": {
|
|
138
|
+
"LanguageKey": {
|
|
139
|
+
"location": "import",
|
|
140
|
+
"path": "./i18n/language-key",
|
|
141
|
+
"id": "src/components/graphwise-reactodia/i18n/language-key.ts::LanguageKey"
|
|
142
|
+
}
|
|
143
|
+
}
|
|
131
144
|
},
|
|
132
145
|
"required": false,
|
|
133
146
|
"optional": false,
|
|
@@ -138,7 +151,7 @@ export class GraphwiseReactodia {
|
|
|
138
151
|
"getter": false,
|
|
139
152
|
"setter": false,
|
|
140
153
|
"reflect": false,
|
|
141
|
-
"defaultValue": "
|
|
154
|
+
"defaultValue": "LanguageKey.EN"
|
|
142
155
|
},
|
|
143
156
|
"providerSettings": {
|
|
144
157
|
"type": "unknown",
|
|
@@ -151,7 +164,7 @@ export class GraphwiseReactodia {
|
|
|
151
164
|
"SparqlDataProviderSettings": {
|
|
152
165
|
"location": "import",
|
|
153
166
|
"path": "@reactodia/workspace",
|
|
154
|
-
"id": "
|
|
167
|
+
"id": "reactodia-workspace/dist/typings/workspace.d.ts::SparqlDataProviderSettings"
|
|
155
168
|
}
|
|
156
169
|
}
|
|
157
170
|
},
|
|
@@ -163,6 +176,25 @@ export class GraphwiseReactodia {
|
|
|
163
176
|
},
|
|
164
177
|
"getter": false,
|
|
165
178
|
"setter": false
|
|
179
|
+
},
|
|
180
|
+
"theme": {
|
|
181
|
+
"type": "string",
|
|
182
|
+
"attribute": "theme",
|
|
183
|
+
"mutable": false,
|
|
184
|
+
"complexType": {
|
|
185
|
+
"original": "string",
|
|
186
|
+
"resolved": "string",
|
|
187
|
+
"references": {}
|
|
188
|
+
},
|
|
189
|
+
"required": false,
|
|
190
|
+
"optional": true,
|
|
191
|
+
"docs": {
|
|
192
|
+
"tags": [],
|
|
193
|
+
"text": "The theme currently applied by the host (e.g. `light`, `dark`). The value itself is never read -\nthe node colors are taken from the host's CSS custom properties - it only signals that those\nvalues have changed. The host owns the theme, including whether it follows the OS color scheme,\nso the change is passed in instead of being detected here."
|
|
194
|
+
},
|
|
195
|
+
"getter": false,
|
|
196
|
+
"setter": false,
|
|
197
|
+
"reflect": false
|
|
166
198
|
}
|
|
167
199
|
};
|
|
168
200
|
}
|
|
@@ -177,6 +209,9 @@ export class GraphwiseReactodia {
|
|
|
177
209
|
}, {
|
|
178
210
|
"propName": "language",
|
|
179
211
|
"methodName": "onLanguageChange"
|
|
212
|
+
}, {
|
|
213
|
+
"propName": "theme",
|
|
214
|
+
"methodName": "onThemeChange"
|
|
180
215
|
}];
|
|
181
216
|
}
|
|
182
217
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import frTranslation from "./fr.reactodia.translation.json";
|
|
2
|
+
import { LanguageKey } from "./language-key";
|
|
3
|
+
// English is provided by default from reactodia so we don't need to include it here, unless we override it
|
|
2
4
|
export const TRANSLATIONS = {
|
|
3
|
-
|
|
5
|
+
[LanguageKey.FR]: frTranslation
|
|
4
6
|
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event name for clearing the local store state of the diagram. There is an event listener listening for it, which
|
|
3
|
+
* will clear the local store state, whenever this event is fired.
|
|
4
|
+
*/
|
|
5
|
+
export const CLEAR_DIAGRAM_STORAGE_EVENT = 'graphwise-reactodia:clear-diagram-storage';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A collection of {@link Subscription}s that can be released together. Components collect the
|
|
3
|
+
* subscriptions they open and call {@link unsubscribeAll} on teardown so nothing is left
|
|
4
|
+
* listening.
|
|
5
|
+
*/
|
|
6
|
+
export class SubscriptionList {
|
|
7
|
+
constructor(subscriptions = []) {
|
|
8
|
+
this.items = subscriptions;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Adds a subscription to the list.
|
|
12
|
+
*/
|
|
13
|
+
add(subscription) {
|
|
14
|
+
this.items.push(subscription);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Adds multiple subscriptions to the list.
|
|
18
|
+
*/
|
|
19
|
+
addAll(subscriptions) {
|
|
20
|
+
this.items.push(...subscriptions);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Removes a subscription from the list without unsubscribing it.
|
|
24
|
+
*/
|
|
25
|
+
remove(subscription) {
|
|
26
|
+
this.items = this.items.filter((sub) => sub !== subscription);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Calls every subscription (unsubscribing it) and clears the list.
|
|
30
|
+
*/
|
|
31
|
+
unsubscribeAll() {
|
|
32
|
+
this.items.forEach((subscription) => subscription());
|
|
33
|
+
this.items = [];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ServiceProvider } from "./service.provider";
|
|
2
|
+
/**
|
|
3
|
+
* Injects the singleton instance of the given service class from the {@link ServiceProvider}.
|
|
4
|
+
*
|
|
5
|
+
* @param serviceClass The class of the service to inject.
|
|
6
|
+
* @returns The shared instance of the specified service class.
|
|
7
|
+
* @template T The type of the service to inject.
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const diagramStateService = service(DiagramStateService);
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export function service(serviceClass) {
|
|
14
|
+
return ServiceProvider.get(serviceClass);
|
|
15
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decorator for a service class.
|
|
3
|
+
* It receives the class and simply type checks it with the {@link Service} interface.
|
|
4
|
+
* Works as a contract, which enforces the service class to have a static ID member.
|
|
5
|
+
* We can't simply implement the interface, since this will enforce the ID to be an instance member, which is not what we want.
|
|
6
|
+
*
|
|
7
|
+
* @param _serviceClass The decorated service class, checked for its ID and discarded.
|
|
8
|
+
* @template T The type of the service instance.
|
|
9
|
+
* @example
|
|
10
|
+
* ```TypeScript
|
|
11
|
+
* @Service
|
|
12
|
+
* export class WindowService {
|
|
13
|
+
* static readonly ID = 'WindowService';
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export function Service(_serviceClass) {
|
|
18
|
+
// no implementation needed. We only use the type checking of the parameter
|
|
19
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider for all {@link Service} instances. Services are singletons: there is only one
|
|
3
|
+
* instance of each service, created on demand and cached here so every consumer shares it.
|
|
4
|
+
*/
|
|
5
|
+
export class ServiceProvider {
|
|
6
|
+
/**
|
|
7
|
+
* Returns the instance of the given service type, creating and caching it on first request.
|
|
8
|
+
*
|
|
9
|
+
* @param serviceClass The service type to retrieve.
|
|
10
|
+
* @returns The shared instance of the service.
|
|
11
|
+
* @template T The type of the service to retrieve.
|
|
12
|
+
*/
|
|
13
|
+
static get(serviceClass) {
|
|
14
|
+
if (!ServiceProvider.SERVICE_INSTANCES.has(serviceClass.ID)) {
|
|
15
|
+
ServiceProvider.SERVICE_INSTANCES.set(serviceClass.ID, new serviceClass());
|
|
16
|
+
}
|
|
17
|
+
return ServiceProvider.SERVICE_INSTANCES.get(serviceClass.ID);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
ServiceProvider.SERVICE_INSTANCES = new Map();
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { createElement } from "react";
|
|
2
2
|
import { createRoot } from "react-dom/client";
|
|
3
|
-
import { DefaultWorkspace, SparqlDataProvider, useLoadedWorkspace, Workspace
|
|
3
|
+
import { DefaultWorkspace, SparqlDataProvider, useLoadedWorkspace, Workspace } from "@reactodia/workspace";
|
|
4
4
|
import { blockingDefaultLayout } from "@reactodia/workspace/layout-sync";
|
|
5
5
|
import { TRANSLATIONS } from "./i18n/translations";
|
|
6
|
+
import { service } from "./providers/service/service-inject";
|
|
7
|
+
import { DiagramService } from "./services/diagram/diagram.service";
|
|
8
|
+
import { DiagramStorageService } from "./services/diagram-storage/diagram-storage.service";
|
|
9
|
+
import { EventService } from "./services/event/event.service";
|
|
10
|
+
import { SubscriptionList } from "./models/subscription-list";
|
|
11
|
+
import { resolveTypeStyle } from "./styles/type-style.resolver";
|
|
6
12
|
/**
|
|
7
13
|
* Reactodia ships English as its built-in default bundle, so English needs no override.
|
|
8
14
|
* Other languages are layered on top as partial bundles; unknown codes fall back to English.
|
|
@@ -20,41 +26,123 @@ function translationsForLanguage(language) {
|
|
|
20
26
|
* runs outside React and therefore cannot call the `useWorkspace` hook.
|
|
21
27
|
*/
|
|
22
28
|
let workspaceContext = null;
|
|
29
|
+
/**
|
|
30
|
+
* Active listeners that persist diagram edits to local storage. Populated when the workspace
|
|
31
|
+
* mounts and released in {@link unmountReactodia}, so persistence follows the same lifecycle as
|
|
32
|
+
* the React root.
|
|
33
|
+
*/
|
|
34
|
+
const subscriptions = new SubscriptionList();
|
|
23
35
|
/**
|
|
24
36
|
* Builds a Reactodia {@link SparqlDataProvider} for the given endpoint using the supplied
|
|
25
37
|
* query preset. The host owns the query configuration and passes it in via props; when none
|
|
26
38
|
* is provided we fall back to Reactodia's generic {@link OwlRdfsSettings} OWL/RDFS preset.
|
|
27
39
|
*/
|
|
28
|
-
function createDataProvider(
|
|
40
|
+
function createDataProvider(props) {
|
|
41
|
+
const { currentRepository, config, providerSettings } = props;
|
|
29
42
|
return new SparqlDataProvider({
|
|
30
43
|
endpointUrl: currentRepository,
|
|
31
44
|
queryMethod: 'POST',
|
|
32
|
-
queryFunction
|
|
33
|
-
},
|
|
45
|
+
queryFunction: config.queryFunction
|
|
46
|
+
}, providerSettings);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Places the seed entities on the canvas. Each IRI is added as a placeholder element, and data is then loaded for the
|
|
50
|
+
* respective nodes using the {@link DataDiagramModel.requestData} method.
|
|
51
|
+
*/
|
|
52
|
+
async function seedIrisToCanvas(context, dataProvider, seed, signal) {
|
|
53
|
+
const { model, performLayout } = context;
|
|
54
|
+
await model.createNewDiagram({ dataProvider, signal });
|
|
55
|
+
for (const iri of seed) {
|
|
56
|
+
model.createElement(iri);
|
|
57
|
+
}
|
|
58
|
+
await model.requestData();
|
|
59
|
+
await performLayout({ signal });
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Places a pre-resolved graph (e.g. a CONSTRUCT query result) on the canvas. The difference between this and
|
|
63
|
+
* {@link seedIrisToCanvas} is that this method does not request link data from the SPARQL endpoint, since describe/construct
|
|
64
|
+
* queries are not persisted in the DB. Here we expect provided links and query for element data only
|
|
65
|
+
*/
|
|
66
|
+
async function seedGraphToCanvas(context, dataProvider, links, signal) {
|
|
67
|
+
const { model, performLayout } = context;
|
|
68
|
+
await model.createNewDiagram({ dataProvider, signal });
|
|
69
|
+
const uniqueIris = new Set();
|
|
70
|
+
links.forEach((link) => {
|
|
71
|
+
var _a;
|
|
72
|
+
uniqueIris.add(link.source);
|
|
73
|
+
uniqueIris.add(link.target);
|
|
74
|
+
// this is a createOrGet, regardless of the name, so we don't need to worry about duplicates.
|
|
75
|
+
model.createElement(link.source);
|
|
76
|
+
model.createElement(link.target);
|
|
77
|
+
(_a = link.rawPredicates) === null || _a === void 0 ? void 0 : _a.forEach((predicate) => {
|
|
78
|
+
model.createLinks({
|
|
79
|
+
linkTypeId: predicate,
|
|
80
|
+
sourceId: link.source,
|
|
81
|
+
targetId: link.target,
|
|
82
|
+
properties: {}
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
await model.requestElementData(Array.from(uniqueIris));
|
|
87
|
+
await performLayout({ signal });
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Subscribes to the model's changes so diagram edits are persisted to local storage. The
|
|
91
|
+
* subscription is tracked in {@link subscriptions} so it is released together with the React
|
|
92
|
+
* root in {@link unmountReactodia}.
|
|
93
|
+
*/
|
|
94
|
+
function onDiagramChange(model) {
|
|
95
|
+
const storage = service(DiagramStorageService);
|
|
96
|
+
const diagramService = service(DiagramService);
|
|
97
|
+
subscriptions.add(diagramService.subscribeToDiagramChange(model, (diagram) => storage.save(diagram)));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Subscribes to the host's "clear persisted diagram" command so the saved layout is dropped on
|
|
101
|
+
* request (e.g. before a fresh seed should take precedence over previously saved edits). The
|
|
102
|
+
* subscription is tracked in {@link subscriptions} so it is released together with the React
|
|
103
|
+
* root in {@link unmountReactodia}.
|
|
104
|
+
*/
|
|
105
|
+
function onClearDiagramStorage() {
|
|
106
|
+
const storage = service(DiagramStorageService);
|
|
107
|
+
const eventService = service(EventService);
|
|
108
|
+
subscriptions.add(eventService.subscribeToClearDiagramStorage(() => storage.clear()));
|
|
34
109
|
}
|
|
35
110
|
/**
|
|
36
111
|
* The Reactodia workspace React component.
|
|
37
112
|
*
|
|
38
113
|
* Authored with `React.createElement` (no TSX) on purpose, since there are differences in stencil and react
|
|
39
114
|
*
|
|
40
|
-
*
|
|
41
|
-
* (Reactodia's `DefaultWorkspace` unified search),
|
|
42
|
-
*
|
|
115
|
+
* Without a seed the canvas starts empty and the user populates it through the workspace
|
|
116
|
+
* search bar (Reactodia's `DefaultWorkspace` unified search), backed by the SPARQL data
|
|
117
|
+
* provider's lookup. With a seed, the seeded nodes are placed on the canvas on startup.
|
|
43
118
|
*/
|
|
44
119
|
function ReactodiaApp(props) {
|
|
45
|
-
const {
|
|
120
|
+
const { language, config } = props;
|
|
121
|
+
const currentDiagram = props.isReload ? exportReactodiaLayout() : undefined;
|
|
46
122
|
const { onMount } = useLoadedWorkspace(async ({ context, signal }) => {
|
|
123
|
+
var _a, _b;
|
|
47
124
|
workspaceContext = context;
|
|
48
125
|
const { model } = context;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
126
|
+
onDiagramChange(model);
|
|
127
|
+
onClearDiagramStorage();
|
|
128
|
+
const dataProvider = createDataProvider(props);
|
|
129
|
+
const isReload = props.isReload;
|
|
130
|
+
const savedDiagram = service(DiagramStorageService).load();
|
|
131
|
+
if (isReload) {
|
|
132
|
+
// Simply reload without using the current state. This may happen when the user switches the language at runtime.
|
|
133
|
+
// since there is no existing mechanism to re-translate the UI at runtime (not for the UI labels at least)
|
|
134
|
+
await model.importLayout({ dataProvider, diagram: currentDiagram, signal, validateLinks: true });
|
|
135
|
+
}
|
|
136
|
+
else if ((_a = config.seedIris) === null || _a === void 0 ? void 0 : _a.length) {
|
|
137
|
+
await seedIrisToCanvas(context, dataProvider, config.seedIris, signal);
|
|
138
|
+
}
|
|
139
|
+
else if ((_b = config.seedGraph) === null || _b === void 0 ? void 0 : _b.length) {
|
|
140
|
+
await seedGraphToCanvas(context, dataProvider, config.seedGraph, signal);
|
|
55
141
|
}
|
|
56
142
|
else {
|
|
57
|
-
|
|
143
|
+
// Restore the previously saved layout when present, otherwise start empty. Either way this binds the
|
|
144
|
+
// data provider (savedDiagram is undefined -> empty diagram), so the unified search/lookup works.
|
|
145
|
+
await model.importLayout({ dataProvider, diagram: savedDiagram, signal, validateLinks: true });
|
|
58
146
|
}
|
|
59
147
|
}, [language]);
|
|
60
148
|
/**
|
|
@@ -71,7 +159,8 @@ function ReactodiaApp(props) {
|
|
|
71
159
|
ref: onMount,
|
|
72
160
|
defaultLayout: blockingDefaultLayout,
|
|
73
161
|
defaultLanguage: language,
|
|
74
|
-
translations: translationsForLanguage(language)
|
|
162
|
+
translations: translationsForLanguage(language),
|
|
163
|
+
typeStyleResolver: resolveTypeStyle
|
|
75
164
|
}, createElement(DefaultWorkspace, {}));
|
|
76
165
|
}
|
|
77
166
|
/**
|
|
@@ -91,7 +180,7 @@ export async function updateReactodia(props) {
|
|
|
91
180
|
return;
|
|
92
181
|
}
|
|
93
182
|
const { model } = workspaceContext;
|
|
94
|
-
const dataProvider = createDataProvider(props
|
|
183
|
+
const dataProvider = createDataProvider(props);
|
|
95
184
|
await model.createNewDiagram({ dataProvider });
|
|
96
185
|
}
|
|
97
186
|
/**
|
|
@@ -105,5 +194,6 @@ export function exportReactodiaLayout() {
|
|
|
105
194
|
* Unmounts the Reactodia application.
|
|
106
195
|
*/
|
|
107
196
|
export function unmountReactodia(root) {
|
|
197
|
+
subscriptions.unsubscribeAll();
|
|
108
198
|
root.unmount();
|
|
109
199
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
4
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else
|
|
6
|
+
for (var i = decorators.length - 1; i >= 0; i--)
|
|
7
|
+
if (d = decorators[i])
|
|
8
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
9
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
10
|
+
};
|
|
11
|
+
var DiagramService_1;
|
|
12
|
+
import { Service } from "../../providers/service/service";
|
|
13
|
+
/**
|
|
14
|
+
* Service for diagram related operations and subscriptions
|
|
15
|
+
*/
|
|
16
|
+
let DiagramService = DiagramService_1 = class DiagramService {
|
|
17
|
+
/**
|
|
18
|
+
* Subscribes to diagram changes. On every debounced change the current layout is
|
|
19
|
+
* exported and passed to the callback.
|
|
20
|
+
* Debounce time for the callback trigger is needed, because certain events (like moving a node) can trigger
|
|
21
|
+
* multiple `historyChanged` events in a short time.
|
|
22
|
+
*
|
|
23
|
+
* @param model The diagram model to observe.
|
|
24
|
+
* @param onChangeCallback Called with the exported layout when the diagram changes.
|
|
25
|
+
* @returns A {@link Subscription} that, when called, removes the listener and drops any
|
|
26
|
+
* pending notification.
|
|
27
|
+
*/
|
|
28
|
+
subscribeToDiagramChange(model, onChangeCallback) {
|
|
29
|
+
let notifyTimer;
|
|
30
|
+
const onHistoryChange = () => {
|
|
31
|
+
if (notifyTimer) {
|
|
32
|
+
clearTimeout(notifyTimer);
|
|
33
|
+
}
|
|
34
|
+
notifyTimer = setTimeout(() => {
|
|
35
|
+
notifyTimer = undefined;
|
|
36
|
+
onChangeCallback(model.exportLayout());
|
|
37
|
+
}, DiagramService_1.NOTIFY_DEBOUNCE_MS);
|
|
38
|
+
};
|
|
39
|
+
model.history.events.on(DiagramService_1.HISTORY_CHANGED_EVENT, onHistoryChange);
|
|
40
|
+
return () => {
|
|
41
|
+
model.history.events.off(DiagramService_1.HISTORY_CHANGED_EVENT, onHistoryChange);
|
|
42
|
+
if (notifyTimer) {
|
|
43
|
+
clearTimeout(notifyTimer);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
DiagramService.ID = 'DiagramService';
|
|
49
|
+
DiagramService.NOTIFY_DEBOUNCE_MS = 300;
|
|
50
|
+
DiagramService.HISTORY_CHANGED_EVENT = 'historyChanged';
|
|
51
|
+
DiagramService = DiagramService_1 = __decorate([
|
|
52
|
+
Service
|
|
53
|
+
], DiagramService);
|
|
54
|
+
export { DiagramService };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
4
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else
|
|
6
|
+
for (var i = decorators.length - 1; i >= 0; i--)
|
|
7
|
+
if (d = decorators[i])
|
|
8
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
9
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
10
|
+
};
|
|
11
|
+
var DiagramStorageService_1;
|
|
12
|
+
import { Service } from "../../providers/service/service";
|
|
13
|
+
import { service } from "../../providers/service/service-inject";
|
|
14
|
+
import { WindowService } from "../window/window.service";
|
|
15
|
+
/**
|
|
16
|
+
* Service for local store-related diagram operations
|
|
17
|
+
*/
|
|
18
|
+
let DiagramStorageService = DiagramStorageService_1 = class DiagramStorageService {
|
|
19
|
+
/**
|
|
20
|
+
* Load the persisted diagram from the local store, or `undefined` when nothing is stored (or the stored value
|
|
21
|
+
* is unreadable).
|
|
22
|
+
*/
|
|
23
|
+
load() {
|
|
24
|
+
const raw = this.storage.getItem(DiagramStorageService_1.STORAGE_KEY);
|
|
25
|
+
if (!raw) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
return JSON.parse(raw);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Persists the given diagram serialization.
|
|
32
|
+
*/
|
|
33
|
+
save(diagram) {
|
|
34
|
+
this.storage.setItem(DiagramStorageService_1.STORAGE_KEY, JSON.stringify(diagram));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Removes the persisted diagram. Used when a new view (a seed) should take precedence over
|
|
38
|
+
* previously saved edits.
|
|
39
|
+
*/
|
|
40
|
+
clear() {
|
|
41
|
+
this.storage.removeItem(DiagramStorageService_1.STORAGE_KEY);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Local storage getter
|
|
45
|
+
*/
|
|
46
|
+
get storage() {
|
|
47
|
+
return service(WindowService).getWindow().localStorage;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
DiagramStorageService.ID = 'DiagramStorageService';
|
|
51
|
+
DiagramStorageService.STORAGE_KEY = 'diagram.state';
|
|
52
|
+
DiagramStorageService = DiagramStorageService_1 = __decorate([
|
|
53
|
+
Service
|
|
54
|
+
], DiagramStorageService);
|
|
55
|
+
export { DiagramStorageService };
|