Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
* This is controlled by new CSS property `--reactodia-dialog-viewport-breakpoint-s` with default value `600px` which makes dialog fill the viewport if the available width is less or equal to that value.
- Allow to override base z-index level for workspace components with a set z-index value via `--reactodia-z-index-base` CSS property;
- Add `changeTransform` event to `CanvasApi.events` which triggers on `CanvasApi.metrics.getTransform()` changes, i.e. when coordinate mapping changes due to scale or canvas size is re-adjusted.
- Add `DiagramModel.cellsVersion` property which updates on every element or link addition/removal/reordering to be able to subscribe to `changeCells` event with `useSyncStore()` hook.
- Deprecate `canvasWidgets` prop on `DefaultWorkspace` and `ClassicWorkspace` in favor of passing widgets directly as children.

#### 🐛 Fixed
Expand Down
13 changes: 7 additions & 6 deletions examples/styleCustomization.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,13 @@ function StyleCustomizationExample() {
function BookDecorations() {
const {model} = Reactodia.useCanvas();

const [, forceUpdate] = React.useState({});
React.useEffect(() => {
const listener = new Reactodia.EventObserver();
listener.listen(model.events, 'changeCells', () => forceUpdate({}));
return () => listener.stopListening();
});
// Update decorations when graph content changes
Reactodia.useSyncStore(
Reactodia.useFrameDebouncedStore(
Reactodia.useEventStore(model.events, 'changeCells')
),
() => model.cellsVersion
);

return model.elements
.filter(element => element instanceof Reactodia.EntityElement)
Expand Down
17 changes: 17 additions & 0 deletions src/diagram/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,18 @@ export class Graph {
private readonly source = new EventSource<GraphEvents>();
readonly events: Events<GraphEvents> = this.source;

private cellsVersion = 1;
private readonly elements = new OrderedMap<Element>();
private readonly links = new OrderedMap<Link>();
private readonly elementLinks = new WeakMap<Element, Link[]>();
private readonly EMPTY_LINKS: Link[] = [];

private readonly linkTypeVisibility = new Map<LinkTypeIri, LinkTypeVisibility>();

getCellsVersion(): number {
return this.cellsVersion;
}

getElements() { return this.elements.items; }
getLinks() { return this.links.items; }

Expand Down Expand Up @@ -86,6 +91,7 @@ export class Graph {

reorderElements(compare: (a: Element, b: Element) => number) {
this.elements.reorder(compare);
this.incrementCellsVersion();
}

getElement(elementId: string): Element | undefined {
Expand All @@ -98,6 +104,7 @@ export class Graph {
}
element.events.onAny(this.onElementEvent);
this.elements.push(element.id, element);
this.incrementCellsVersion();
this.source.trigger('changeCells', {updateAll: false, changedElement: element});
}

Expand All @@ -116,6 +123,7 @@ export class Graph {
}
this.elements.delete(elementId);
element.events.offAny(this.onElementEvent);
this.incrementCellsVersion();
this.source.trigger('changeCells', {updateAll: false, changedElement: element, changedLinks});
}
}
Expand Down Expand Up @@ -151,6 +159,7 @@ export class Graph {

link.events.onAny(this.onLinkEvent);
this.links.push(link.id, link);
this.incrementCellsVersion();
this.source.trigger('changeCells', {updateAll: false, changedLinks: [link]});
}

Expand All @@ -163,6 +172,7 @@ export class Graph {
if (link) {
link.events.offAny(this.onLinkEvent);
this.removeLinkReferences(link);
this.incrementCellsVersion();
if (!(options && options.silent)) {
this.source.trigger('changeCells', {updateAll: false, changedLinks: [link]});
}
Expand Down Expand Up @@ -193,6 +203,13 @@ export class Graph {
}
}

private incrementCellsVersion(): void {
this.cellsVersion = this.cellsVersion + 1;
if (this.cellsVersion >= Number.MAX_SAFE_INTEGER) {
this.cellsVersion = 1;
}
}

get linkVisibility(): ReadonlyMap<LinkTypeIri, LinkTypeVisibility> {
return this.linkTypeVisibility;
}
Expand Down
9 changes: 9 additions & 0 deletions src/diagram/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ export interface GraphStructure {
* to create RDF terms for identifiers and property values.
*/
get factory(): Rdf.DataFactory;
/**
* Graph content (elements and links) version number which changes on every cell change
* (when element or link added/removed/reordered, see {@link DiagramModelEvents.changeCells}).
*/
get cellsVersion(): number;
/**
* All elements (nodes) in the graph.
*/
Expand Down Expand Up @@ -205,6 +210,10 @@ export class DiagramModel implements GraphStructure {
return this.getTermFactory();
}

get cellsVersion(): number {
return this.graph.getCellsVersion();
}

get elements(): ReadonlyArray<Element> {
return this.graph.getElements();
}
Expand Down
30 changes: 13 additions & 17 deletions src/widgets/visualAuthoring/visualAuthoring.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import * as React from 'react';

import { EventObserver } from '../../coreUtils/events';
import { useObservedProperty } from '../../coreUtils/hooks';
import {
useEventStore, useFrameDebouncedStore, useObservedProperty, useSyncStore,
} from '../../coreUtils/hooks';
import { Debouncer } from '../../coreUtils/scheduler';

import type { ElementModel, LinkModel } from '../../data/model';
Expand Down Expand Up @@ -312,25 +314,19 @@ function EntityDecoratorsInner(props: {
const {inlineActions} = props;
const {model, editor} = useWorkspace();

const inAuthoringMode = useObservedProperty(editor.events, 'changeMode', () => editor.inAuthoringMode);

const [version, setVersion] = React.useState(0);
React.useEffect(() => {
const debouncer = new Debouncer();
const listener = new EventObserver();
const scheduleUpdate = () => setVersion(v => v + 1);
listener.listen(model.events, 'changeCells', () => {
debouncer.call(scheduleUpdate);
});
return () => {
listener.stopListening();
debouncer.dispose();
};
}, []);
const inAuthoringMode = useObservedProperty(
editor.events,
'changeMode',
() => editor.inAuthoringMode
);
const cellsVersion = useSyncStore(
useFrameDebouncedStore(useEventStore(model.events, 'changeCells')),
() => model.cellsVersion
);

const cachedDecorators = React.useMemo(
() => new WeakMap<EntityElement, React.ReactNode>(),
[inlineActions, version]
[inlineActions, cellsVersion]
);

const decorators: React.ReactNode[] = [];
Expand Down
Loading