diff --git a/docs/components/visual-authoring.md b/docs/components/visual-authoring.md
index 277ec436..3e88129e 100644
--- a/docs/components/visual-authoring.md
+++ b/docs/components/visual-authoring.md
@@ -4,7 +4,7 @@ title:
# Visual Authoring
-[`
`](/docs/api/workspace/functions/VisualAuthoring) component is a [canvas widget](/docs/components/canvas.md) to provide UI for the [visual graph authoring](/docs/concepts/graph-authoring.md).
+[`
`](/docs/api/workspace/functions/VisualAuthoring) component is a [canvas widget](/docs/components/canvas.md#widgets) to provide UI for the [visual graph authoring](/docs/concepts/graph-authoring.md).
:::important
`
` widget must be provided to the canvas to in order to display visual graph authoring UI.
diff --git a/docs/components/workspace.md b/docs/components/workspace.md
index 2be401e8..886335ea 100644
--- a/docs/components/workspace.md
+++ b/docs/components/workspace.md
@@ -16,9 +16,9 @@ title:
The UI of the workspace is defined by the child components provided to the `
`:
-Default built-in layout is provided as [`
`](/docs/api/workspace/functions/DefaultWorkspace) component which includes the [unified search](/docs/components/unified-search.md), main menu and action [toolbars](/docs/components/toolbar.md) and all built-in [canvas widgets](/docs/components/canvas.md).
+Default built-in layout is provided as [`
`](/docs/api/workspace/functions/DefaultWorkspace) component which includes the [unified search](/docs/components/unified-search.md), main menu and action [toolbars](/docs/components/toolbar.md) and all built-in [canvas widgets](/docs/components/canvas.md#widgets).
-Alternative (classic) built-in layout is provided as [`
`](/docs/api/workspace/functions/ClassicWorkspace) component which uses [layout panels](/docs/components/workspace-layout-panels.md) with [class tree](/docs/components/class-tree.md), [instances search](/docs/components/instances-search.md), [link types toolbox](/docs/components/link-types-toolbox.md), [`
`](/docs/api/workspace/functions/ClassicToolbar) and all built-in [canvas widgets](/docs/components/canvas.md).
+Alternative (classic) built-in layout is provided as [`
`](/docs/api/workspace/functions/ClassicWorkspace) component which uses [layout panels](/docs/components/workspace-layout-panels.md) with [class tree](/docs/components/class-tree.md), [instances search](/docs/components/instances-search.md), [link types toolbox](/docs/components/link-types-toolbox.md), [`
`](/docs/api/workspace/functions/ClassicToolbar) and all built-in [canvas widgets](/docs/components/canvas.md#widgets).
When providing a custom workspace layout it is required to use [`
`](/docs/api/workspace/functions/WorkspaceRoot) as a top-level parent component to establish necessary style defaults, including styles for light or dark [color scheme](/docs/concepts/design-system.mdx).
diff --git a/docs/concepts/canvas-overlays.md b/docs/concepts/canvas-overlays.md
index 2fed7e07..8e37663f 100644
--- a/docs/concepts/canvas-overlays.md
+++ b/docs/concepts/canvas-overlays.md
@@ -67,6 +67,7 @@ The component look can be customized using the following CSS properties (see [de
| `--reactodia-dialog-border-color` | Border color for the dialog (uses the base border color if not set). |
| `--reactodia-dialog-border-radius` | Border radius for the dialog (uses the base border radius if not set). |
| `--reactodia-dialog-border-width` | Border width for the dialog (uses the base border width if not set). |
+| `--reactodia-dialog-viewport-breakpoint-s` | Makes the dialog fill the viewport if the available width is less or equal to that value. |
## Overlay tasks
diff --git a/docs/concepts/command-history.md b/docs/concepts/command-history.md
index b4540dcf..92ca6251 100644
--- a/docs/concepts/command-history.md
+++ b/docs/concepts/command-history.md
@@ -74,7 +74,10 @@ function Component() {
const element = model.createElement(iri);
// In other cases the command needs to be executed explicitly
- batch.history.execute(setElementState(element, {'my:custom:state': 42}));
+ batch.history.execute(setElementState(
+ element,
+ element.elementState.set(MyCustomProperty, 42)
+ ));
}
batch.store();
@@ -114,7 +117,9 @@ function exchangeElementPositions(
function resetElementStateForAll(
elements: readonly Reactodia.ElementElement[]
): Command {
- const commands = elements.map(el => Reactodia.setElementState(el, undefined));
+ const commands = elements.map(el =>
+ Reactodia.setElementState(el, TemplateState.empty)
+ );
return Command.compound('Reset state for elements', commands);
}
```
diff --git a/docs/concepts/data-provider.md b/docs/concepts/data-provider.md
index a8310518..bc1bbb75 100644
--- a/docs/concepts/data-provider.md
+++ b/docs/concepts/data-provider.md
@@ -122,14 +122,10 @@ function ExampleRdfProviderProvisionFromJGF() {
dataProvider.addGraph(triples);
await model.createNewDiagram({dataProvider, signal});
-
- const elementIris: Reactodia.ElementIri[] = [];
for (const {element} of await dataProvider.lookup({elementTypeId: 'graph:type:Person'})) {
- elementIris.push(model.createElement(element).iri);
+ model.createElement(element.id);
}
-
- await model.requestElementData(elementIris);
- await model.requestLinks();
+ await model.requestData();
await performLayout({signal});
}, []);
diff --git a/docs/concepts/design-system.mdx b/docs/concepts/design-system.mdx
index 0383de10..7b47741c 100644
--- a/docs/concepts/design-system.mdx
+++ b/docs/concepts/design-system.mdx
@@ -282,6 +282,13 @@ function Spacing() {
}
```
+## Z-index
+
+The following properties allow to override base `z-index` level for workspace components with a set `z-index` value:
+```css
+--reactodia-z-index-base
+```
+
## Controls
### Buttons
diff --git a/docs/concepts/event-system.md b/docs/concepts/event-system.md
index 2575a252..c290d8c6 100644
--- a/docs/concepts/event-system.md
+++ b/docs/concepts/event-system.md
@@ -82,6 +82,8 @@ function Component() {
In the above example, [`Reactodia.useFrameDebouncedStore()`](/docs/api/workspace/functions/useFrameDebouncedStore.md) hook is used to debounce React component updates due to triggered events from the event store to only once each rendered frame based on [`requestAnimationFrame()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame).
+Alternatively, [`Reactodia.useLayerDebouncedStore()`](/docs/api/workspace/functions/useLayerDebouncedStore.md) can be used for debounce store updates until the next canvas layer update or explicit [`RenderingState.syncUpdate()`](/docs/api/workspace/interfaces/RenderingState.md#syncupdate) call to force all layers to be rendered in order (e.g. to read element sizes or other dependent rendering state).
+
## Making an observable
To create an observable instance it would be enough to implement the [`Events`](/docs/api/workspace/interfaces/Events) interface. An easiest way to do it would be to use [`EventSource`](/docs/api/workspace/classes/EventSource):
diff --git a/docs/concepts/graph-model.md b/docs/concepts/graph-model.md
index a0c40bb5..ee98c2b0 100644
--- a/docs/concepts/graph-model.md
+++ b/docs/concepts/graph-model.md
@@ -14,10 +14,6 @@ Each **element** is identified by a [generated ID](/docs/api/workspace/classes/E
Each **link** is identified by a [generated ID](/docs/api/workspace/classes/Link.md#generateid), has source and target element IDs, and a [link type IRI](/docs/api/workspace/type-aliases/LinkTypeIri.md). In its state it stores path geometry as a [collection of points](/docs/api/workspace/classes/Link.md#vertices) (in [paper coordinates](/docs/concepts/canvas-coordinates)) and an arbitrary persisted [link state](/docs/api/workspace/classes/Link.md#linkstate).
-:::note
-Currently there is only one concrete element type besides the data graph ones: [`VoidElement`](/docs/api/workspace/classes/VoidElement.md) which is displayed as nothing (empty point) but can be connected to with the links.
-:::
-
## Data graph
While elements and links are considered to be diagram graph nodes and edges, some of them can represent entities and relations from (external) data graph defined by the [`DataProvider`](/docs/concepts/data-provider):
@@ -39,6 +35,25 @@ Basically, a sub-graph of a full external data graph composed of entities and re
Using the [graph authoring](/docs/concepts/graph-authoring) feature it is possible to work on the changes to the data graph and apply it once ready.
+The library provides [default templates](/docs/components/canvas.md#customization) and UI support ([`
`](/docs/components/visual-authoring.md)) for entity elements and relation links.
+
+## Annotations
+
+Additional elements and links can be added to the diagram content to annotate other graph elements:
+
+| Cell type | Description |
+|-----------|-------------|
+| [`AnnotationElement`](/docs/api/workspace/classes/AnnotationElement.md) | Diagram-only annotation element, [exported and imported](#import-and-export) with the diagram. |
+| [`AnnotationLink`](/docs/api/workspace/classes/AnnotationLink.md) | Diagram-only annotation link, [exported and imported](#import-and-export) with the diagram. |
+
+These elements and links behave as normal graph nodes and edges i.e. participate in [graph layout](/docs/concepts/graph-layout.md), can be [selected](/docs/components/selection.md), etc. At the same time, annotation elements and links do not have any data from [data graph](#data-graph), have user-editable content stored in the [`template state`](/docs/api/workspace/classes/Element.md#elementstate) and can be created, changed or deleted independently from the [graph authoring mode](/docs/concepts/graph-authoring.md).
+
+The library provides [default templates](/docs/components/canvas.md#customization) and UI support ([`
`](/docs/components/annotation-support.md)) for annotations.
+
+:::note
+The library also defines utility element type [`VoidElement`](/docs/api/workspace/classes/VoidElement.md) which is displayed as nothing (empty point) but can be connected to with the links.
+:::
+
## Manipulating the diagram
To access and manipulate the diagram one can use [diagram model](/docs/api/workspace/classes/DataDiagramModel.md) from [`WorkspaceContext`](/docs/concepts/workspace-context):
@@ -142,5 +157,7 @@ function WorkingWithImportExport() {
```
:::note
-Currently only [data graph](#data-graph) elements and links (including groups) can be exported and imported.
+**Unstable**: Additional element and link cell types can be imported using `elementCellTypes` and `linkCellTypes` options for [`DataDiagramModel.importLayout()`](/docs/api/workspace/classes/DataDiagramModel.md#importlayout).
+
+See [`SerializableElementCell`](/docs/api/workspace/interfaces/SerializableElementCell.md) and [`SerializableLinkCell`](/docs/api/workspace/interfaces/SerializableLinkCell.md) contracts for diagram cell to be serializable.
:::
diff --git a/docs/concepts/keyboard-hotkeys.md b/docs/concepts/keyboard-hotkeys.md
index 2890a6b9..4258bca8 100644
--- a/docs/concepts/keyboard-hotkeys.md
+++ b/docs/concepts/keyboard-hotkeys.md
@@ -29,7 +29,7 @@ Example hotkey strings: `Ctrl+Alt+K`, `Alt+Meta+Q`, `Ctrl+/`, `None+G`.
## How to define a hotkey
-At high level, various Reactodia components provide a prop for a hotkey string to perform a certain action, see [`
`](/docs/components/toolbar.md), [`
`](/docs/components/selection.md):
+At high level, various Reactodia components provide a prop for a hotkey string to perform a certain action, see [`
`](/docs/components/toolbar.md), [`
`](/docs/components/selection.md#selecting-elements) and [`
`](/docs/components/selection.md#selecting-links):
```tsx
{/* Default hotkeys for the built-in components: */}
diff --git a/docs/concepts/workspace-context.md b/docs/concepts/workspace-context.md
index 08c4d1bf..e0d11deb 100644
--- a/docs/concepts/workspace-context.md
+++ b/docs/concepts/workspace-context.md
@@ -53,17 +53,15 @@ function MyWidget() {
// Use workspace context
}
-Reactodia.defineCanvasWidget(MyWidget, element => ({element, attachment: 'viewport'}));
-
function Example() {
const {defaultLayout} = Reactodia.useWorker(Layouts);
return (
]}
- />
+ search={null}>
+
+
);
}
diff --git a/docs/examples/live.md b/docs/examples/live.md
index 9cb49394..6aa59f32 100644
--- a/docs/examples/live.md
+++ b/docs/examples/live.md
@@ -9,39 +9,37 @@ Workspace usage example via live editor block which updates on every change.
```tsx live
function SimpleExample() {
- const GRAPH_DATA =
- 'https://raw.githubusercontent.com/reactodia/reactodia-workspace/' +
- 'master/examples/resources/orgOntology.ttl';
-
- const {defaultLayout} = Reactodia.useWorker(Layouts);
-
- const {onMount} = Reactodia.useLoadedWorkspace(async ({context, signal}) => {
- const {model, performLayout} = context;
- // Fetch graph data to use as underlying data source
- const response = await fetch(GRAPH_DATA, {signal});
- const graphData = new N3.Parser().parse(await response.text());
- const dataProvider = new Reactodia.RdfDataProvider({acceptBlankNodes: false});
- dataProvider.addGraph(graphData);
-
- // Create empty diagram and put owl:Class entities with links between them
- await model.createNewDiagram({dataProvider, signal});
- const elementTypeId = 'http://www.w3.org/2002/07/owl#Class';
- for (const {element} of await dataProvider.lookup({elementTypeId})) {
- model.createElement(element);
- }
- await model.requestLinksOfType();
-
- // Layout elements on canvas
- await performLayout({signal});
- }, []);
-
- return (
-
-
-
-
-
- );
+ const GRAPH_DATA = 'https://reactodia.github.io/resources/orgOntology.ttl';
+
+ const {defaultLayout} = Reactodia.useWorker(Layouts);
+
+ const {onMount} = Reactodia.useLoadedWorkspace(async ({context, signal}) => {
+ const {model, performLayout} = context;
+ // Fetch graph data to use as underlying data source
+ const response = await fetch(GRAPH_DATA, {signal});
+ const graphData = new N3.Parser().parse(await response.text());
+ const dataProvider = new Reactodia.RdfDataProvider({acceptBlankNodes: false});
+ dataProvider.addGraph(graphData);
+
+ // Create empty diagram and put owl:Class entities with links between them
+ await model.createNewDiagram({dataProvider, signal});
+ const elementTypeId = 'http://www.w3.org/2002/07/owl#Class';
+ for (const {element} of await dataProvider.lookup({elementTypeId})) {
+ model.createElement(element.id);
+ }
+ await model.requestData();
+
+ // Layout elements on canvas
+ await performLayout({signal});
+ }, []);
+
+ return (
+
+
+
+
+
+ );
}
```
diff --git a/docusaurus.config.ts b/docusaurus.config.ts
index c686d9d9..3ee438a2 100644
--- a/docusaurus.config.ts
+++ b/docusaurus.config.ts
@@ -2,6 +2,8 @@ import { themes as prismThemes } from 'prism-react-renderer';
import type { Config } from '@docusaurus/types';
import type * as Preset from '@docusaurus/preset-classic';
+const libraryPathPrefix = process.env.CI ? `.` : '..';
+
const config: Config = {
title: 'Reactodia',
tagline: 'Visual interaction with graph data in a form of a diagram.',
@@ -52,12 +54,12 @@ const config: Config = {
'docusaurus-plugin-typedoc',
{
entryPoints: [
- '../reactodia-workspace/src/workspace.ts',
- '../reactodia-workspace/src/layout-sync.ts',
- '../reactodia-workspace/src/layout.worker.ts',
- '../reactodia-workspace/src/legacy-styles.tsx',
+ `${libraryPathPrefix}/reactodia-workspace/src/workspace.ts`,
+ `${libraryPathPrefix}/reactodia-workspace/src/layout-sync.ts`,
+ `${libraryPathPrefix}/reactodia-workspace/src/layout.worker.ts`,
+ `${libraryPathPrefix}/reactodia-workspace/src/legacy-styles.tsx`,
],
- tsconfig: '../reactodia-workspace/tsconfig.typings.json',
+ tsconfig: `${libraryPathPrefix}/reactodia-workspace/tsconfig.typings.json`,
readme: 'none',
hideGroupHeadings: true,
excludePrivate: true,
diff --git a/package-lock.json b/package-lock.json
index 275b6a42..77cb4f1a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,19 +1,20 @@
{
"name": "@reactodia/reactodia.github.io",
- "version": "0.30.1",
+ "version": "0.31.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@reactodia/reactodia.github.io",
- "version": "0.30.1",
+ "version": "0.31.0",
"dependencies": {
"@docusaurus/core": "3.8.1",
"@docusaurus/preset-classic": "3.8.1",
"@docusaurus/theme-live-codeblock": "^3.8.1",
"@easyops-cn/docusaurus-search-local": "^0.51.0",
"@mdx-js/react": "^3.1.0",
- "@reactodia/workspace": "^0.30.1",
+ "@reactodia/hashmap": "^0.2.1",
+ "@reactodia/workspace": "^0.31.0",
"clsx": "^2.1.1",
"n3": "^1.17.2",
"prism-react-renderer": "^2.4.1",
@@ -24,6 +25,8 @@
"@docusaurus/module-type-aliases": "3.8.1",
"@docusaurus/tsconfig": "3.8.1",
"@docusaurus/types": "3.8.1",
+ "@rdfjs/types": "^1.1.2",
+ "@types/n3": "^1.26.1",
"@vscode/codicons": "^0.0.36",
"docusaurus-plugin-typedoc": "^1.4.0",
"source-map-loader": "^5.0.0",
@@ -4725,10 +4728,20 @@
"integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==",
"license": "MIT"
},
+ "node_modules/@rdfjs/types": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rdfjs/types/-/types-1.1.2.tgz",
+ "integrity": "sha512-wqpOJK1QCbmsGNtyzYnojPU8gRDPid2JO0Q0kMtb4j65xhCK880cnKAfEOwC+dX85VJcCByQx5zOwyyfCjDJsg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@reactodia/hashmap": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/@reactodia/hashmap/-/hashmap-0.1.0.tgz",
- "integrity": "sha512-hUbi0THBLDDjNe0hTmOeCnZJJbaFIZSAocbCxpyAUIM7O/KQMk302JrkEj+nbobHF9C664IgR6GT+/EkPnzyPg==",
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/@reactodia/hashmap/-/hashmap-0.2.1.tgz",
+ "integrity": "sha512-SruXxvsqPQcszDlwYgPCM1SKZ0Ds6cTEpFzxpex51KYpLdCJ1bXjrqVyQq7kLEXNmLNBLIj+j6WtHCVy0D68+w==",
"license": "MIT"
},
"node_modules/@reactodia/worker-proxy": {
@@ -4738,12 +4751,12 @@
"license": "MIT"
},
"node_modules/@reactodia/workspace": {
- "version": "0.30.1",
- "resolved": "https://registry.npmjs.org/@reactodia/workspace/-/workspace-0.30.1.tgz",
- "integrity": "sha512-aQ4hmIwGGa8Up++o3+IBlVodsP511HMNbcj0WMDAQlQ3b2mKZM+yHGzCT6vbrOVBC/joWrNy7LlXvoTY4wykMA==",
+ "version": "0.31.0",
+ "resolved": "https://registry.npmjs.org/@reactodia/workspace/-/workspace-0.31.0.tgz",
+ "integrity": "sha512-GC+r0Rnc9DYSzlKYDJqNW4ffCfbqvKemR/ONV1jo3SFm6nfA82lwHU7eoFTDiAkeQe5rS4WgBZHc/t3/Qs3lTA==",
"license": "LGPL-2.1-or-later",
"dependencies": {
- "@reactodia/hashmap": "^0.1.0",
+ "@reactodia/hashmap": "^0.2.1",
"@reactodia/worker-proxy": "^0.1.0",
"clsx": "^2.1.1",
"d3-color": "^3.1.0",
@@ -5365,6 +5378,17 @@
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
"license": "MIT"
},
+ "node_modules/@types/n3": {
+ "version": "1.26.1",
+ "resolved": "https://registry.npmjs.org/@types/n3/-/n3-1.26.1.tgz",
+ "integrity": "sha512-TilYHzpU6ecXVJAbV+6o17Z8ZkWLWx6ZJD3IluaU4RiGHxqjU2or9fopxFHS6iXS6qcl5Mg1K3wSx9L8xxJaJQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rdfjs/types": "*",
+ "@types/node": "*"
+ }
+ },
"node_modules/@types/node": {
"version": "24.0.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.0.tgz",
diff --git a/package.json b/package.json
index 94d0cbfa..5002a8f2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@reactodia/reactodia.github.io",
- "version": "0.30.1",
+ "version": "0.31.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
@@ -20,7 +20,8 @@
"@docusaurus/theme-live-codeblock": "^3.8.1",
"@easyops-cn/docusaurus-search-local": "^0.51.0",
"@mdx-js/react": "^3.1.0",
- "@reactodia/workspace": "^0.30.1",
+ "@reactodia/hashmap": "^0.2.1",
+ "@reactodia/workspace": "^0.31.0",
"clsx": "^2.1.1",
"n3": "^1.17.2",
"prism-react-renderer": "^2.4.1",
@@ -31,6 +32,8 @@
"@docusaurus/module-type-aliases": "3.8.1",
"@docusaurus/tsconfig": "3.8.1",
"@docusaurus/types": "3.8.1",
+ "@rdfjs/types": "^1.1.2",
+ "@types/n3": "^1.26.1",
"@vscode/codicons": "^0.0.36",
"docusaurus-plugin-typedoc": "^1.4.0",
"source-map-loader": "^5.0.0",
diff --git a/sidebars.ts b/sidebars.ts
index e547be8d..64caf9c4 100644
--- a/sidebars.ts
+++ b/sidebars.ts
@@ -49,6 +49,7 @@ const sidebars: SidebarsConfig = {
link: {
type: 'generated-index',
},
+ collapsed: false,
items: [{type: 'autogenerated', dirName: 'components'}],
},
{
diff --git a/src/examples/ExampleCommon.tsx b/src/examples/ExampleCommon.tsx
index 334ee223..4ed7f5a0 100644
--- a/src/examples/ExampleCommon.tsx
+++ b/src/examples/ExampleCommon.tsx
@@ -1,4 +1,5 @@
import * as React from 'react';
+import { HashMap } from '@reactodia/hashmap';
import * as Reactodia from '@reactodia/workspace';
import { saveAs } from 'file-saver';
@@ -12,8 +13,18 @@ export function ExampleToolbarMenu() {
onSelect={async file => {
const preloadedElements = new Map
();
for (const element of model.elements) {
- for (const data of Reactodia.iterateEntitiesOf(element)) {
- preloadedElements.set(data.id, data);
+ for (const entity of Reactodia.iterateEntitiesOf(element)) {
+ preloadedElements.set(entity.id, entity);
+ }
+ }
+
+ const preloadedLinks = new HashMap(
+ Reactodia.hashLink,
+ Reactodia.equalLinks
+ );
+ for (const link of model.links) {
+ for (const relation of Reactodia.iterateRelationsOf(link)) {
+ preloadedLinks.set(relation, relation);
}
}
@@ -25,6 +36,7 @@ export function ExampleToolbarMenu() {
dataProvider: model.dataProvider,
diagram: diagramLayout,
preloadedElements,
+ preloadedLinks,
validateLinks: true,
});
} catch (err) {
diff --git a/src/examples/ExampleMetadata.ts b/src/examples/ExampleMetadata.ts
index 951908f1..ac162ebe 100644
--- a/src/examples/ExampleMetadata.ts
+++ b/src/examples/ExampleMetadata.ts
@@ -19,35 +19,53 @@ const rdfs = vocabulary('http://www.w3.org/2000/01/rdf-schema#', [
const SIMULATED_DELAY: number = 50; /* ms */
export class ExampleMetadataProvider extends Reactodia.BaseMetadataProvider {
- private readonly propertyTypes = [owl.AnnotationProperty, owl.DatatypeProperty, owl.ObjectProperty];
+ private readonly propertyTypes = [
+ owl.AnnotationProperty,
+ owl.DatatypeProperty,
+ owl.ObjectProperty,
+ ];
private readonly editableTypes = new Set([owl.Class, ...this.propertyTypes]);
- private readonly editableRelations = new Set([rdfs.domain, rdfs.range]);
- private readonly literalLanguages: ReadonlyArray = ['de', 'en', 'es', 'ru', 'zh'];
+ private readonly editableRelations = new Set([
+ rdfs.domain,
+ rdfs.range,
+ ]);
+ private readonly literalLanguages: ReadonlyArray =
+ ['de', 'en', 'es', 'ru', 'zh'];
constructor() {
super({
getLiteralLanguages: () => this.literalLanguages,
createEntity: async (type, {signal}) => {
await Reactodia.delay(SIMULATED_DELAY, {signal});
- const random32BitDigits = Math.floor((1 + Math.random()) * 0x100000000).toString(16).substring(1);
+ const random32BitDigits = Math.floor((1 + Math.random()) * 0x100000000)
+ .toString(16).substring(1);
const typeLabel = Reactodia.Rdf.getLocalName(type) ?? 'Entity';
return {
- id: `${type}_${random32BitDigits}`,
- types: [type],
- properties: {
- [Reactodia.rdfs.label]: [
- Reactodia.Rdf.DefaultDataFactory.literal(`New ${typeLabel}`)
- ]
+ data: {
+ id: `${type}_${random32BitDigits}`,
+ types: [type],
+ properties: {
+ [Reactodia.rdfs.label]: [
+ Reactodia.Rdf.DefaultDataFactory.literal(`New ${typeLabel}`)
+ ]
+ },
},
+ elementState: type === owl.Class
+ ? Reactodia.TemplateState.empty.set(
+ Reactodia.TemplateProperties.Expanded, true
+ )
+ : undefined,
};
},
createRelation: async (source, target, linkType, {signal}) => {
await Reactodia.delay(SIMULATED_DELAY, {signal});
return {
- sourceId: source.id,
- targetId: target.id,
- linkTypeId: linkType,
- properties: {},
+ data: {
+ sourceId: source.id,
+ targetId: target.id,
+ linkTypeId: linkType,
+ properties: {},
+ },
};
},
canConnect: async (source, target, linkType, {signal}) => {
diff --git a/src/examples/PlaygroundBasic.tsx b/src/examples/PlaygroundBasic.tsx
index 224c8526..0a65dfbf 100644
--- a/src/examples/PlaygroundBasic.tsx
+++ b/src/examples/PlaygroundBasic.tsx
@@ -23,9 +23,9 @@ export function PlaygroundBasic() {
await model.createNewDiagram({dataProvider, signal});
const elementTypeId = 'http://www.w3.org/2002/07/owl#Class';
for (const {element} of await dataProvider.lookup({elementTypeId})) {
- model.createElement(element);
+ model.createElement(element.id);
}
- await model.requestLinks();
+ await model.requestData();
// Layout elements on canvas
await performLayout({signal});
diff --git a/src/examples/PlaygroundClassicWorkspace.tsx b/src/examples/PlaygroundClassicWorkspace.tsx
index 58c16966..ff92c17c 100644
--- a/src/examples/PlaygroundClassicWorkspace.tsx
+++ b/src/examples/PlaygroundClassicWorkspace.tsx
@@ -53,17 +53,17 @@ export function PlaygroundClassicWorkspace() {
typeStyleResolver={SemanticTypeStyles}>
{
+ elementTemplateResolver: (types, element) => {
if (types.includes('http://www.w3.org/2002/07/owl#DatatypeProperty')) {
return Reactodia.ClassicTemplate;
}
return undefined;
},
- linkTemplateResolver: type => {
- if (type === 'http://www.w3.org/2000/01/rdf-schema#subClassOf') {
- return Reactodia.DefaultLinkTemplate;
+ linkTemplateResolver: (linkType, link) => {
+ if (linkType === 'http://www.w3.org/2000/01/rdf-schema#subClassOf') {
+ return Reactodia.StandardLinkTemplate;
}
- return OntologyLinkTemplates(type);
+ return OntologyLinkTemplates(linkType);
},
}}
toolbar={{
diff --git a/src/examples/PlaygroundGraphAuthoring.tsx b/src/examples/PlaygroundGraphAuthoring.tsx
index 504182f8..444273be 100644
--- a/src/examples/PlaygroundGraphAuthoring.tsx
+++ b/src/examples/PlaygroundGraphAuthoring.tsx
@@ -43,18 +43,18 @@ export function PlaygroundGraphAuthoring() {
await model.importLayout({dataProvider, signal});
if (dataSource.type === 'url') {
- const elements = [
- model.createElement('http://www.w3.org/ns/org#Organization'),
- model.createElement('http://www.w3.org/ns/org#FormalOrganization'),
- model.createElement('http://www.w3.org/ns/org#hasMember'),
- model.createElement('http://www.w3.org/ns/org#hasSubOrganization'),
- model.createElement('http://www.w3.org/ns/org#subOrganizationOf'),
- model.createElement('http://www.w3.org/ns/org#unitOf'),
+ const entities = [
+ 'http://www.w3.org/ns/org#Organization',
+ 'http://www.w3.org/ns/org#FormalOrganization',
+ 'http://www.w3.org/ns/org#hasMember',
+ 'http://www.w3.org/ns/org#hasSubOrganization',
+ 'http://www.w3.org/ns/org#subOrganizationOf',
+ 'http://www.w3.org/ns/org#unitOf',
];
- await Promise.all([
- model.requestElementData(elements.map(el => el.iri)),
- model.requestLinks(),
- ]);
+ for (const entity of entities) {
+ model.createElement(entity);
+ }
+ await model.requestData();
await performLayout({signal});
} else {
getCommandBus(Reactodia.UnifiedSearchTopic)
@@ -91,7 +91,10 @@ export function PlaygroundGraphAuthoring() {
class RenameSubclassOfProvider extends Reactodia.RenameLinkToLinkStateProvider {
override canRename(link: Reactodia.Link): boolean {
- return link.typeId === 'http://www.w3.org/2000/01/rdf-schema#subClassOf';
+ return (
+ link instanceof Reactodia.AnnotationLink ||
+ link.typeId === 'http://www.w3.org/2000/01/rdf-schema#subClassOf'
+ );
}
}
diff --git a/src/examples/PlaygroundSparql.tsx b/src/examples/PlaygroundSparql.tsx
index caf456e7..417d0616 100644
--- a/src/examples/PlaygroundSparql.tsx
+++ b/src/examples/PlaygroundSparql.tsx
@@ -53,15 +53,6 @@ export function PlaygroundSparql() {
defaultLayout={defaultLayout}>
}
- canvasWidgets={[
-
-
-
- ]}
languages={[
{code: 'de', label: 'Deutsch'},
{code: 'en', label: 'English'},
@@ -73,8 +64,14 @@ export function PlaygroundSparql() {
{code: 'pt', label: 'português'},
{code: 'ru', label: 'Русский'},
{code: 'zh', label: '汉语'},
- ]}
- />
+ ]}>
+
+
+
+
);
}
diff --git a/src/examples/PlaygroundStressTest.tsx b/src/examples/PlaygroundStressTest.tsx
index 7a68c15c..8b40d506 100644
--- a/src/examples/PlaygroundStressTest.tsx
+++ b/src/examples/PlaygroundStressTest.tsx
@@ -44,10 +44,7 @@ export function PlaygroundStressTest(props: {
}));
}
batch.store();
- await Promise.all([
- model.requestElementData(nodes),
- model.requestLinks(),
- ]);
+ await model.requestData();
model.history.reset();
const canvas = view.findAnyCanvas();
diff --git a/src/examples/PlaygroundStyleCustomization.tsx b/src/examples/PlaygroundStyleCustomization.tsx
index 4e10d1ec..b9ce755d 100644
--- a/src/examples/PlaygroundStyleCustomization.tsx
+++ b/src/examples/PlaygroundStyleCustomization.tsx
@@ -66,13 +66,16 @@ export function PlaygroundStyleCustomization() {
}
return undefined;
},
- linkTemplateResolver: type => DoubleArrowLinkTemplate,
+ linkTemplateResolver: (linkType, link) => {
+ if (linkType) {
+ return DoubleArrowLinkTemplate;
+ }
+ return undefined;
+ },
}}
- canvasWidgets={[
-
- ]}
- menu={}
- />
+ menu={}>
+
+
);
}
@@ -114,14 +117,22 @@ function ElementLabelDecoration(props: { target: Reactodia.EntityElement }) {
}
function BookDecorations() {
- const {model} = Reactodia.useCanvas();
+ const {canvas, model} = Reactodia.useCanvas();
+
+ // Update decorations when graph content changes
+ Reactodia.useSyncStore(
+ Reactodia.useLayerDebouncedStore(
+ Reactodia.useEventStore(model.events, 'changeCells'),
+ canvas.renderingState
+ ),
+ () => model.cellsVersion
+ );
+
return model.elements
.filter(element => element instanceof Reactodia.EntityElement)
.map(element => );
}
-Reactodia.defineCanvasWidget(BookDecorations, element => ({element, attachment: 'viewport'}));
-
// External element decoration, i.e. rendered outside the element template
function BookDecoration(props: { target: Reactodia.EntityElement }) {
const {target} = props;
@@ -167,7 +178,7 @@ const DoubleArrowLinkTemplate: Reactodia.LinkTemplate = {
height: 12,
},
renderLink: props => (
-