Enable link functionality in GUI2 Topology View
Change-Id: I1b88080ecdf8c9b6f8a60af4832a12441186d508
diff --git a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.css b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.css
index cf965ac..57a2bd3 100644
--- a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.css
+++ b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.css
@@ -19,7 +19,7 @@
ONOS GUI -- Topology View (forces device visual) -- CSS file
*/
g.node.device rect {
- fill: #f0f0f0;
+ fill: #f0f0f070;
}
g.node.device text {
fill: #bbb;
@@ -30,7 +30,7 @@
g.node.device.online rect {
- fill: #ffffff;
+ fill: #fafafad0;
}
g.node.device.online text {
fill: #3c3a3a;
diff --git a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.html b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.html
index e329874..70d4d5b 100644
--- a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.html
+++ b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.html
@@ -13,17 +13,75 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<svg:g xmlns:svg="http://www.w3.org/2000/svg" #devSvg
+<svg:defs xmlns:svg="http://www.w3.org/2000/svg">
+ <!-- Template explanation: Define an SVG Filter that in
+ line 1) render the target object in to a bit map and apply a blur to it
+ based on its alpha channel
+ line 2) take that blurred layer and shift it down and to the right by 4
+ line 3) Merge this blurred and shifted layer and overlay it with the
+ original target object
+ -->
+ <svg:filter id="drop-shadow">
+ <svg:feGaussianBlur in="SourceAlpha" stdDeviation="2" result="blur" />
+ <svg:feOffset in="blur" dx="4" dy="4" result="offsetBlur"/>
+ <svg:feMerge >
+ <svg:feMergeNode in="offsetBlur" />
+ <svg:feMergeNode in="SourceGraphic" />
+ </svg:feMerge>
+ </svg:filter>
+ <svg:linearGradient id="diagonal_blue" x1="0%" y1="0%" x2="100%" y2="100%">
+ <svg:stop offset= "0%" style="stop-color: #5b99d2;" />
+ <svg:stop offset= "100%" style="stop-color: #3b79b2;" />
+ </svg:linearGradient>
+</svg:defs>
+<!-- Template explanation: Creates an SVG Group and in
+ line 1) transform it to the position calculated by the d3 force graph engine
+ line 2) Give it various CSS styles depending on attributes
+ line 3) When it is clicked, call the method that toggles the selection and
+ emits an event.
+ Other child objects have their own description
+-->
+<svg:g xmlns:svg="http://www.w3.org/2000/svg"
[attr.transform]="'translate(' + device?.x + ',' + device?.y + '), scale(' + scale + ')'"
[ngClass]="['node', 'device', device.online?'online':'', selected?'selected':'']"
(click)="toggleSelected(device)">
- <svg:rect class="node-container" x="-18" y="-18" width="36" height="36">
- <!--[attr.width]="devText.getComputedTextLength()+36" -->
- <!--[@deviceLabelToggle]="{ value: labelToggle, params: {txtWidth: devSvg.getBBox().width+'px' }}">-->
+ <svg:desc>Device {{device.id}}</svg:desc>
+ <!-- Template explanation: Creates an SVG Rectangle and in
+ line 1) set a css style and shift so that it's centred
+ line 2) set the initial width and height - width changes with label
+ line 3) link to the animation 'deviceLabelToggle', pass in to it a width
+ calculated from the width of the text, and additional padding at the end
+ line 4) Apply the filter defined above to this rectangle (even as its
+ width changes
+ -->
+ <svg:rect
+ class="node-container" x="-18" y="-18"
+ width="36" height="36"
+ [@deviceLabelToggle]="{ value: labelToggle, params: {txtWidth: (36 + labelTextLen() * 1.05)+'px' }}"
+ filter= "url(#drop-shadow)">
</svg:rect>
- <svg:rect x="-16" y="-16" width="32" height="32" [ngStyle]="{'fill': 'rgb(91, 153, 210)'}">
+ <!-- Template explanation: Creates an SVG Rectangle slightly smaller and
+ overlaid on the above. This is the blue box, and its width and height does
+ not change
+ -->
+ <svg:rect x="-16" y="-16" width="32" height="32" style="fill: url(#diagonal_blue)">
</svg:rect>
- <svg:text #devText text-anchor="start" y="0.3em" x="22" [ngStyle]="{'transform': 'scale(' + scale + ')'}">
+ <!-- Template explanation: Creates an SVG Text element and in
+ line 1) make it left aligned and slightly down and to the right of the last rect
+ line 2) set its text length to be the calculated value - see that function
+ line 3) because of kerning the actual text might be shorter or longer than
+ the pre-calculated value - if so change the spacing between the letters
+ (and not the letter width to compensate)
+ line 4) link to the animation deviceLabelToggleTxt, so that the text appears
+ in gently
+ line 5) The text will be one of 3 values - blank, the id or the name
+ -->
+ <svg:text
+ text-anchor="start" y="0.3em" x="22"
+ [attr.textLength]= "labelTextLen()"
+ lengthAdjust= "spacing"
+ [ngStyle]="{'transform': 'scale(' + scale + ')'}"
+ [@deviceLabelToggleTxt]="labelToggle">
{{ labelToggle == 0 ? '': labelToggle == 1 ? device.id:device.props.name }}
</svg:text>
<!-- It's not possible to drive the following xref dynamically -->
diff --git a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.spec.ts b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.spec.ts
index 0f4feee..32ed2ef 100644
--- a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.spec.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.spec.ts
@@ -21,6 +21,7 @@
import {of} from 'rxjs';
import {ChangeDetectorRef} from '@angular/core';
import {Device} from '../../models';
+import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
class MockActivatedRoute extends ActivatedRoute {
constructor(params: Params) {
@@ -44,6 +45,7 @@
testDevice.online = true;
TestBed.configureTestingModule({
+ imports: [ BrowserAnimationsModule ],
declarations: [ DeviceNodeSvgComponent ],
providers: [
{ provide: LogService, useValue: logSpy },
diff --git a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.ts b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.ts
index 036fc08..756769d 100644
--- a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.ts
@@ -14,17 +14,18 @@
* limitations under the License.
*/
import {
+ ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
- ElementRef, EventEmitter,
+ EventEmitter,
Input,
OnChanges, Output,
SimpleChanges,
- ViewChild
} from '@angular/core';
-import {Node, Device, LabelToggle} from '../../models';
+import {Device, LabelToggle, UiElement} from '../../models';
import {LogService} from 'gui2-fw-lib';
import {NodeVisual} from '../nodevisual';
+import {animate, state, style, transition, trigger} from '@angular/animations';
/**
* The Device node in the force graph
@@ -36,28 +37,38 @@
selector: '[onos-devicenodesvg]',
templateUrl: './devicenodesvg.component.html',
styleUrls: ['./devicenodesvg.component.css'],
- // changeDetection: ChangeDetectionStrategy.Default,
- // animations: [
- // trigger('deviceLabelToggle', [
- // state('0', style({ // none
- // width: '36px',
- // })),
- // state('1, 2', // id
- // style({ width: '{{ txtWidth }}'}),
- // { params: {'txtWidth': '36px'}}
- // ), // default
- // transition('0 => *', animate('1000ms ease-in')),
- // transition('* => 0', animate('1000ms ease-out'))
- // ])
- // ]
+ changeDetection: ChangeDetectionStrategy.Default,
+ animations: [
+ trigger('deviceLabelToggle', [
+ state('0', style({ // none
+ width: '36px',
+ })),
+ state('1, 2', // id
+ style({ width: '{{ txtWidth }}'}),
+ { params: {'txtWidth': '36px'}}
+ ), // default
+ transition('0 => 1', animate('250ms ease-in')),
+ transition('1 => 2', animate('250ms ease-in')),
+ transition('* => 0', animate('250ms ease-out'))
+ ]),
+ trigger('deviceLabelToggleTxt', [
+ state('0', style( {
+ opacity: 0,
+ })),
+ state( '1,2', style({
+ opacity: 1.0
+ })),
+ transition('0 => 1', animate('250ms ease-in')),
+ transition('* => 0', animate('250ms ease-out'))
+ ])
+ ]
})
export class DeviceNodeSvgComponent extends NodeVisual implements OnChanges {
@Input() device: Device;
@Input() scale: number = 1.0;
@Input() labelToggle: LabelToggle = LabelToggle.NONE;
- @Output() selectedEvent = new EventEmitter<Node>();
+ @Output() selectedEvent = new EventEmitter<UiElement>();
textWidth: number = 36;
- @ViewChild('idTxt') idTxt: ElementRef;
constructor(
protected log: LogService,
private ref: ChangeDetectorRef
@@ -84,4 +95,31 @@
}
this.ref.markForCheck();
}
+
+ /**
+ * Calculate the text length in advance as well as possible
+ *
+ * The length of SVG text cannot be exactly estimated, because depending on
+ * the letters kerning might mean that it is shorter or longer than expected
+ *
+ * This takes the approach of 8px width per letter of this size, that on average
+ * evens out over words. A word like 'ilj' will be much shorter than 'wm0'
+ * because of kerning
+ *
+ *
+ * In addition in the template, the <svg:text> properties
+ * textLength and lengthAdjust ensure that the text becomes long with extra
+ * wide spacing created as necessary.
+ *
+ * Other approaches like getBBox() of the text
+ */
+ labelTextLen() {
+ if (this.labelToggle === 1) {
+ return this.device.id.length * 8 * this.scale;
+ } else if (this.labelToggle === 2 && this.device && this.device.props.name && this.device.props.name.trim().length > 0) {
+ return this.device.props.name.length * 8 * this.scale;
+ } else {
+ return 0;
+ }
+ }
}