diff --git a/examples/circlegraph/css/diagram.css b/examples/circlegraph/css/diagram.css
index 3918cb22..d5a88d44 100644
--- a/examples/circlegraph/css/diagram.css
+++ b/examples/circlegraph/css/diagram.css
@@ -38,13 +38,4 @@
.sprotty-node.selected {
stroke: #dd8;
stroke-width: 6;
-}
-
-.sprotty-missing {
- stroke-width: 1;
- stroke: #f00;
- fill: #f00;
- font-family: SansSerif;
- font-size: 14pt;
- text-anchor: middle;
}
\ No newline at end of file
diff --git a/examples/classdiagram/css/diagram.css b/examples/classdiagram/css/diagram.css
index a04f3357..63b9e045 100644
--- a/examples/classdiagram/css/diagram.css
+++ b/examples/classdiagram/css/diagram.css
@@ -123,14 +123,6 @@
stroke-width: 1;
}
-.sprotty-missing {
- stroke-width: 1;
- stroke: #f00;
- fill: #f00;
- font-size: 14pt;
- text-anchor: middle;
-}
-
.sprotty-popup-title {
font-weight: bold;
margin-bottom: 10px;
diff --git a/examples/multicore/css/diagram.css b/examples/multicore/css/diagram.css
index 131bf15e..089924f3 100644
--- a/examples/multicore/css/diagram.css
+++ b/examples/multicore/css/diagram.css
@@ -62,14 +62,6 @@
text-align: start;
}
-.sprotty-missing {
- stroke-width: 1;
- stroke: #f00;
- fill: #f00;
- font-size: 14pt;
- text-anchor: middle;
-}
-
.sprotty-popup-title {
font-weight: bold;
margin-bottom: 10px;
diff --git a/packages/sprotty/css/sprotty.css b/packages/sprotty/css/sprotty.css
index 1a04b7fe..002795d9 100644
--- a/packages/sprotty/css/sprotty.css
+++ b/packages/sprotty/css/sprotty.css
@@ -93,3 +93,11 @@
.animation-spin {
animation: spin 1.5s linear infinite;
}
+
+.sprotty-missing {
+ stroke-width: 1;
+ stroke: #f00;
+ fill: #f00;
+ font-size: 14pt;
+ text-anchor: start;
+}
\ No newline at end of file
diff --git a/packages/sprotty/src/base/views/view.spec.ts b/packages/sprotty/src/base/views/view.spec.ts
index 880707c1..03488e5b 100644
--- a/packages/sprotty/src/base/views/view.spec.ts
+++ b/packages/sprotty/src/base/views/view.spec.ts
@@ -45,7 +45,7 @@ describe('base views', () => {
it('missing view', () => {
const vnode = missingView.render(emptyRoot, context);
- expect(toHTML(vnode)).to.be.equal('?EMPTY?');
+ expect(toHTML(vnode)).to.be.equal('missing "NONE" view');
const model = new SNodeImpl();
model.bounds = {
x: 42,
@@ -56,7 +56,7 @@ describe('base views', () => {
model.id = 'foo';
model.type = 'type';
const vnode1 = missingView.render(model, context);
- expect(toHTML(vnode1)).to.be.equal('?foo?');
+ expect(toHTML(vnode1)).to.be.equal('missing "type" view');
});
});
diff --git a/packages/sprotty/src/base/views/view.tsx b/packages/sprotty/src/base/views/view.tsx
index 6643ae2a..ac2ae94b 100644
--- a/packages/sprotty/src/base/views/view.tsx
+++ b/packages/sprotty/src/base/views/view.tsx
@@ -17,7 +17,7 @@
/** @jsx svg */
import { svg } from '../../lib/jsx';
-import { injectable, multiInject, optional, interfaces } from 'inversify';
+import { injectable, multiInject, optional, interfaces, inject } from 'inversify';
import { VNode } from 'snabbdom';
import { TYPES } from '../types';
import { InstanceRegistry } from '../../utils/registry';
@@ -26,6 +26,7 @@ import { SModelElementImpl, SModelRootImpl, SParentElementImpl } from '../model/
import { EMPTY_ROOT, CustomFeatures } from '../model/smodel-factory';
import { registerModelElement } from '../model/smodel-utils';
import { Point } from 'sprotty-protocol';
+import { ILogger } from '../../utils/logging';
/**
* Arguments for `IView` rendering.
@@ -94,6 +95,9 @@ export type ViewRegistrationFactory = () => ViewRegistration;
*/
@injectable()
export class ViewRegistry extends InstanceRegistry {
+
+ @inject(TYPES.ILogger) protected logger: ILogger;
+
constructor(@multiInject(TYPES.ViewRegistration) @optional() registrations: ViewRegistration[]) {
super();
this.registerDefaults();
@@ -107,6 +111,7 @@ export class ViewRegistry extends InstanceRegistry {
}
override missing(key: string): IView {
+ this.logger.warn(this, `no registered view for type '${key}', please configure a view in the ContainerModule`);
return new MissingView();
}
}
@@ -155,8 +160,20 @@ export class EmptyView implements IView {
*/
@injectable()
export class MissingView implements IView {
+ private static positionMap = new Map();
+
render(model: Readonly, context: RenderingContext): VNode {
- const position: Point = (model as any).position || Point.ORIGIN;
- return ?{model.id}?;
+ const position: Point = (model as any).position || this.getPostion(model.type);
+ return missing "{model.type}" view;
+ }
+
+ getPostion(type: string) {
+ let position = MissingView.positionMap.get(type);
+ if (!position) {
+ position = Point.ORIGIN;
+ MissingView.positionMap.forEach(value => position = value.y >= position!.y ? {x: 0, y: value.y + 20} : position);
+ MissingView.positionMap.set(type, position);
+ }
+ return position;
}
}