Skip to content
This repository was archived by the owner on May 14, 2025. It is now read-only.

Commit b9508ac

Browse files
BoykoAlexoodamien
authored andcommitted
Adopt JointJS 2.x for Flo
1 parent 3ddfe7e commit b9508ac

File tree

12 files changed

+100
-124
lines changed

12 files changed

+100
-124
lines changed

ui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
"web-animations-js": "2.3.1",
4949
"stompjs": "2.3.3",
5050
"jshint": "2.9.5",
51-
"spring-flo": "git://github.com/spring-projects/spring-flo#483faf1a1b6ed595580a2af86d901d3f33013353",
51+
"spring-flo": "git://github.com/spring-projects/spring-flo#9f3c37be645a96d6073bc42299cefa6afd52d7a4",
5252
"ng-busy": "1.4.4"
5353
},
5454
"devDependencies": {

ui/src/app/shared/flo/flo.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ $spring-grey: #DFE5E8;
7171
stroke-width: 3;
7272
}
7373

74+
/* Otherwise ports, link labels, embedded shapes get outline on focus */
75+
svg * {
76+
outline: none;
77+
}
78+
7479
.input-port {
7580
cursor: pointer;
7681
}

ui/src/app/streams/components/flo/editor.service.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,16 +253,16 @@ describe('editor.service', () => {
253253
}
254254
}
255255

256-
function expectMarkerCount(markers: Map<string, Array<Flo.Marker>>, expectedCount: number) {
256+
function expectMarkerCount(markers: Map<string | number, Array<Flo.Marker>>, expectedCount: number) {
257257
let count = 0;
258258
Array.from(markers.keys()).forEach((k) => {
259259
count += markers.get(k).length;
260260
});
261261
expect(count).toEqual(expectedCount);
262262
}
263263

264-
function printMarkers(markers: Map<string, Array<Flo.Marker>>) {
265-
LoggerService.log('Markers summary: ' + markers.size + ' map entries');
264+
function printMarkers(markers: Map<string | number, Array<Flo.Marker>>) {
265+
LoggerService.log('Markers summary: ' + markers.size + ' map entries');
266266
Array.from(markers.keys()).forEach((k) => {
267267
const values = markers.get(k);
268268
LoggerService.log('For ' + k + ' there are ' + values.length + ' markers');
@@ -273,7 +273,7 @@ describe('editor.service', () => {
273273
});
274274
}
275275

276-
function getMarkersOn(markers: Map<string, Array<Flo.Marker>>, node: dia.Element): Flo.Marker[] {
276+
function getMarkersOn(markers: Map<string | number, Array<Flo.Marker>>, node: dia.Element): Flo.Marker[] {
277277
return markers.get(node.id);
278278
}
279279

ui/src/app/streams/components/flo/editor.service.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { Injectable } from '@angular/core';
1818
import { BsModalService } from 'ngx-bootstrap';
1919
import { ApplicationType } from '../../../shared/model/application-type';
2020
import { Flo, Constants } from 'spring-flo';
21-
import { dia } from 'jointjs';
21+
import { dia, g } from 'jointjs';
2222
import { StreamPropertiesDialogComponent } from './properties/stream-properties-dialog.component';
2323
import { Utils } from './support/utils';
2424
import * as _joint from 'jointjs';
@@ -49,6 +49,10 @@ export class EditorService implements Flo.Editor {
4949

5050
constructor(private bsModalService: BsModalService) {}
5151

52+
interactive: dia.CellView.InteractivityOptions = {
53+
vertexAdd: false
54+
};
55+
5256
createHandles(flo: Flo.EditorContext, createHandle: (owner: dia.CellView, kind: string,
5357
action: () => void, location: dia.Point) => void, owner: dia.CellView): void {
5458
if (owner.model instanceof joint.dia.Link) {
@@ -58,12 +62,12 @@ export class EditorService implements Flo.Editor {
5862
const bbox = element.getBBox();
5963

6064
// Delete handle
61-
let pt = (<any>bbox).origin().offset(bbox.width + 3, bbox.height + 3);
65+
let pt = bbox.origin().offset(bbox.width + 3, bbox.height + 3);
6266
createHandle(owner, Constants.REMOVE_HANDLE_TYPE, flo.deleteSelectedNode, pt);
6367

6468
// Properties handle
6569
if (!element.attr('metadata/unresolved')) {
66-
pt = (<any>bbox).origin().offset(-14, bbox.height + 3);
70+
pt = bbox.origin().offset(-14, bbox.height + 3);
6771
createHandle(owner, Constants.PROPERTIES_HANDLE_TYPE, () => {
6872
const modalRef = this.bsModalService.show(StreamPropertiesDialogComponent);
6973
modalRef.content.title = `Properties for ${element.attr('metadata/name').toUpperCase()}`;
@@ -195,7 +199,7 @@ export class EditorService implements Flo.Editor {
195199
}
196200

197201
calculateDragDescriptor(flo: Flo.EditorContext, draggedView: dia.CellView, viewUnderMouse: dia.CellView,
198-
point: dia.Point, sourceComponent: string): Flo.DnDDescriptor {
202+
point: g.Point, sourceComponent: string): Flo.DnDDescriptor {
199203
const source = draggedView.model;
200204
const paper = flo.getPaper();
201205
const targetUnderMouse = viewUnderMouse ? viewUnderMouse.model : undefined;
@@ -250,7 +254,7 @@ export class EditorService implements Flo.Editor {
250254
if ((type === 'input' && targetHasIncomingPort && hasOutgoingPort)
251255
|| (type === 'output' && targetHasOutgoingPort && hasIncomingPort)) {
252256
const bbox = joint.V(magnet).bbox(false, paper.viewport);
253-
const distance = (<any>point).distance({
257+
const distance = point.distance({
254258
x: bbox.x + bbox.width / 2,
255259
y: bbox.y + bbox.height / 2
256260
});
@@ -594,9 +598,9 @@ export class EditorService implements Flo.Editor {
594598
});
595599
}
596600

597-
validate(graph: dia.Graph, dsl: string, flo: Flo.EditorContext): Promise<Map<string, Flo.Marker[]>> {
601+
validate(graph: dia.Graph, dsl: string, flo: Flo.EditorContext): Promise<Map<string | number, Flo.Marker[]>> {
598602
return new Promise(resolve => {
599-
const markers: Map<string, Array<Flo.Marker>> = new Map();
603+
const markers: Map<string | number, Array<Flo.Marker>> = new Map();
600604
const promises: Promise<void>[] = [];
601605
graph.getElements().filter(e => !e.get('parent') && e.attr('metadata')).forEach(e => {
602606
promises.push(new Promise<void>((nodeFinished) => {

ui/src/app/streams/components/flo/render.service.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ export class RenderService implements Flo.Renderer {
172172
/*&& graph.getConnectedLinks(oldSource, {outbound: true}).length === 0*/) {
173173
// No outgoing links -> hide stream name label
174174
// Set silently, last attr call would refresh the view
175-
(<any>oldSource).attr('.stream-label/display', 'none', {silent: true});
175+
oldSource.attr('.stream-label/display', 'none', {silent: true});
176176

177177
// // Can't remove attr and update the view because port marking is being wiped out, so set 'block' display
178178
// oldSource.attr('.input-port/display', 'block');
@@ -181,7 +181,7 @@ export class RenderService implements Flo.Renderer {
181181
if (newSource && newSource.attr('metadata/name') === 'destination') {
182182
// Has outgoing link, there shouldn't be any incoming links yet -> show stream name label
183183
// Set silently, last attr call would refresh the view
184-
(<any>newSource).attr('.stream-label/display', 'block', {silent: true});
184+
newSource.attr('.stream-label/display', 'block', {silent: true});
185185

186186
// newSource.attr('.input-port/display', 'none');
187187
}
@@ -210,7 +210,7 @@ export class RenderService implements Flo.Renderer {
210210

211211
// No more incoming links, there shouldn't be any outgoing links yet -> indeterminate, hide stream label
212212
// Set silently, last attr call would refresh the view
213-
(<any>oldTarget).attr('.stream-label/display', 'none', {silent: true});
213+
oldTarget.attr('.stream-label/display', 'none', {silent: true});
214214

215215
// // Can't remove attr and update the view because port marking is being wiped out, so set 'block' display
216216
// oldTarget.attr('.output-port/display', 'block');
@@ -221,7 +221,7 @@ export class RenderService implements Flo.Renderer {
221221
if (newTarget.attr('metadata/name') === 'destination') {
222222
// Incoming link -> hide stream name label
223223
// Set silently, last attr call would refresh the view
224-
(<any>newTarget).attr('.stream-label/display', 'none', {silent: true});
224+
newTarget.attr('.stream-label/display', 'none', {silent: true});
225225

226226
// // new target is destination? Hide output port then.
227227
// newTarget.attr('.output-port/display', 'none');
@@ -250,7 +250,7 @@ export class RenderService implements Flo.Renderer {
250250
&& graph.getConnectedLinks(source, {outbound: true}).length === 0) {
251251
// No more outgoing links, can't be any incoming links yet -> indeterminate, hide stream name label
252252
// Set silently, last attr call would refresh the view
253-
(<any>source).attr('.stream-label/display', 'none', {silent: true});
253+
source.attr('.stream-label/display', 'none', {silent: true});
254254
source.removeAttr('.input-port/display');
255255
view = flo.getPaper().findViewByModel(source);
256256
if (view) {
@@ -261,7 +261,7 @@ export class RenderService implements Flo.Renderer {
261261
&& graph.getConnectedLinks(target, {inbound: true}).length === 0) {
262262
// No more incoming links, there shouldn't be any outgoing links yet -> leave stream label hidden
263263
// Set silently, last attr call would refresh the view
264-
(<any>target).attr('.stream-label/display', 'none', {silent: true});
264+
target.attr('.stream-label/display', 'none', {silent: true});
265265
target.removeAttr('.output-port/display');
266266
view = flo.getPaper().findViewByModel(target);
267267
if (view) {
@@ -374,7 +374,7 @@ export class RenderService implements Flo.Renderer {
374374
if (target && target.attr('metadata/name') === 'destination') {
375375
// Incoming link has been added -> hide stream label
376376
// Set silently because update will be called for the next property setting
377-
(<any>target).attr('.stream-label/display', 'none', {silent: true});
377+
target.attr('.stream-label/display', 'none', {silent: true});
378378
// XXX target.attr('.output-port/display', 'none');
379379
}
380380
// If tap link has been added update the stream-label for the target

ui/src/app/streams/components/flo/support/layout.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export function layout(paper: dia.Paper) {
77

88
let gridSize = paper.options.gridSize;
99
if (gridSize <= 1) {
10-
gridSize = IMAGE_H / 2;
10+
gridSize = IMAGE_H;
1111
}
1212

1313
const g = new dagre.graphlib.Graph();

ui/src/app/streams/components/flo/support/shapes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,9 +256,9 @@ joint.shapes.flo.InstanceDot = joint.shapes.basic.Generic.extend({
256256

257257
defaults: joint.util.deepSupplement({
258258
type: TYPE_INSTANCE_DOT,
259-
size: { width: 60, height: 60 },
259+
size: { width: 6, height: 6 },
260260
attrs: {
261-
'circle': { r: 30, transform: 'translate(30, 30)' }
261+
'circle': { r: 3, transform: 'translate(3, 3)' }
262262
}
263263
}, joint.shapes.basic.Generic.prototype.defaults)
264264
});

ui/src/app/streams/components/flo/support/view-helper.ts

Lines changed: 49 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -71,39 +71,44 @@ export class ViewHelper {
7171
},
7272
renderLabels: function () {
7373

74-
if (!this._V.labels) {
75-
return this;
74+
const cache = this._V;
75+
let vLabels = cache.labels;
76+
const labelCache = this._labelCache = {};
77+
const labelSelectors = this._labelSelectors = {};
78+
79+
if (vLabels) {
80+
vLabels.empty();
7681
}
7782

7883
if (this._angularComponentRef) {
7984
Object.keys(this._angularComponentRef).forEach(k => this._angularComponentRef[k].destroy());
8085
this._angularComponentRef = {};
8186
}
8287

83-
this._labelCache = {};
84-
const $labels = $(this._V.labels.node).empty();
85-
86-
const labels = this.model.get('labels') || [];
87-
if (!labels.length) {
88+
const model = this.model;
89+
const labels = model.get('labels') || [];
90+
const labelsCount = labels.length;
91+
if (labelsCount === 0) {
8892
return this;
8993
}
9094

91-
const labelTemplate = joint.util.template(this.model.get('labelMarkup') || this.model.labelMarkup);
92-
// This is a prepared instance of a vectorized SVGDOM node for the label element resulting from
93-
// compilation of the labelTemplate. The purpose is that all labels will just `clone()` this
94-
// node to create a duplicate.
95-
const labelNodeInstance = V(labelTemplate());
95+
if (!vLabels) {
96+
// there is no label container in the markup but some labels are defined
97+
// add a <g class="labels" /> container
98+
vLabels = cache.labels = V('g').addClass('labels').appendTo(this.el);
99+
}
96100

97-
const canLabelMove = this.can('labelMove');
101+
for (let i = 0; i < labelsCount; i++) {
98102

99-
_.each(labels, function (label: any, idx) {
103+
const label = labels[i];
100104

101-
let labelNode;
105+
let node;
106+
let selectors;
102107

103108
if (componentFactoryResolver && LINK_LABEL_COMPONENT_TYPE.has(label.type)) {
104109
// Inject link label component and take its DOM
105-
if (this._angularComponentRef && this._angularComponentRef[idx]) {
106-
this._angularComponentRef[idx].destroy();
110+
if (this._angularComponentRef && this._angularComponentRef[i]) {
111+
this._angularComponentRef[i].destroy();
107112
}
108113

109114
const nodeComponentFactory = componentFactoryResolver
@@ -115,72 +120,50 @@ export class ViewHelper {
115120
this._angularComponentRef = {};
116121
}
117122

118-
this._angularComponentRef[idx] = componentRef;
119-
this._angularComponentRef[idx].changeDetectorRef.markForCheck();
123+
this._angularComponentRef[i] = componentRef;
124+
this._angularComponentRef[i].changeDetectorRef.markForCheck();
120125

121126
applicationRef.attachView(componentRef.hostView);
122127
componentRef.instance.data = label;
123-
this._angularComponentRef[idx].changeDetectorRef.detectChanges();
128+
this._angularComponentRef[i].changeDetectorRef.detectChanges();
124129

125-
labelNode = this._angularComponentRef[idx].location.nativeElement.children.item(0);
130+
node = this._angularComponentRef[i].location.nativeElement.children.item(0);
131+
selectors = {};
126132
} else {
127133
// Default JointJS behaviour
128-
labelNode = labelNodeInstance.clone().node;
129-
}
130-
131-
V(labelNode).attr('label-idx', idx);
132-
if (canLabelMove) {
133-
V(labelNode).attr('cursor', 'move');
134-
}
135-
136-
// Cache label nodes so that the `updateLabels()` can just update the label node positions.
137-
this._labelCache[idx] = V(labelNode);
134+
const labelMarkup = this._normalizeLabelMarkup(this._getLabelMarkup(label.markup));
135+
if (labelMarkup) {
136+
node = labelMarkup.node;
137+
selectors = labelMarkup.selectors;
138138

139-
const $text = $(labelNode).find('text');
140-
const $rect = $(labelNode).find('rect');
139+
} else {
140+
const builtinDefaultLabel = model._builtins.defaultLabel;
141+
const builtinDefaultLabelMarkup = this._normalizeLabelMarkup(this._getLabelMarkup(builtinDefaultLabel.markup));
141142

142-
// Text attributes with the default `text-anchor` and font-size set.
143-
const textAttributes = _.extend({
144-
'text-anchor': 'middle',
145-
'font-size': 14
146-
}, joint.util.getByPath(label, 'attrs/text', '/'));
143+
const defaultLabel = model._getDefaultLabel();
144+
const defaultLabelMarkup = this._normalizeLabelMarkup(this._getLabelMarkup(defaultLabel.markup));
147145

148-
$text.attr(_.omit(textAttributes, 'text'));
149-
150-
if (!_.isUndefined(textAttributes.text)) {
151-
152-
V($text[0]).text(textAttributes.text + '', {annotations: textAttributes.annotations});
146+
const defaultMarkup = defaultLabelMarkup || builtinDefaultLabelMarkup;
153147

148+
node = defaultMarkup.node;
149+
selectors = defaultMarkup.selectors;
150+
}
154151
}
155152

156-
// Note that we first need to append the `<text>` element to the DOM in order to
157-
// get its bounding box.
158-
$labels.append(labelNode);
159-
160-
// `y-alignment` - center the text element around its y coordinate.
161-
const textBbox = V($text[0]).bbox(true, $labels[0]);
162-
V($text[0]).translate(0, -textBbox.height / 2);
163153

164-
// Add default values.
165-
const rectAttributes = _.extend({
154+
const vLabel = V(node);
155+
vLabel.attr('label-idx', i); // assign label-idx
156+
vLabel.appendTo(vLabels);
157+
labelCache[i] = vLabel; // cache node for `updateLabels()` so it can just update label node positions
166158

167-
fill: 'white',
168-
rx: 3,
169-
ry: 3
170-
171-
}, joint.util.getByPath(label, 'attrs/rect', '/'));
172-
173-
const padding = 1;
159+
selectors[this.selector] = vLabel.node;
160+
labelSelectors[i] = selectors; // cache label selectors for `updateLabels()`
161+
}
174162

175-
$rect.attr(_.extend(rectAttributes, {
176-
x: textBbox.x - padding,
177-
y: textBbox.y - padding - textBbox.height / 2, // Take into account the y-alignment translation.
178-
width: textBbox.width + 2 * padding,
179-
height: textBbox.height + 2 * padding
180-
}));
163+
this.updateLabels();
181164

182-
}, this);
183165
return this;
166+
184167
}
185168
});
186169
}

0 commit comments

Comments
 (0)