import { Component, ContentChild, Input, OnInit, QueryList, TemplateRef, AfterViewInit, ViewChildren, ElementRef, OnDestroy, ViewChild } from '@angular/core'; import * as d3 from 'd3'; import LeaderLine from 'leader-line-new'; import { gradeColors } from '../../../styles/colors'; @Component({ selector: 'onguard-sequence-diagram', templateUrl: './sequence-diagram.component.html', styleUrls: ['./sequence-diagram.component.scss'] }) export class SequenceDiagramComponent implements OnInit, AfterViewInit, OnDestroy { @Input() forward: any[]; @Input() reverse?: any[]; @Input() scrollMatchFactor = 1; @ContentChild('item') itemTemplate: TemplateRef; @ViewChildren('sequenceItems') items: QueryList>; @ViewChild('lineContainer') lineContainer: ElementRef; @ViewChild('lineSpacer') lineSpacer: ElementRef; // Keep track of lines lines: LeaderLine[] = []; @Input() lineOptions = { startSocketGravity: -1, startPlug: 'arrow2', endPlug: 'disc', path: 'arc', size: 3 } as LeaderLine.Options; @Input() labelOptions = { color: '#8CA3AD', fontFamily: 'Source Sans Pro', fontSize: '16px' }; constructor() { } ngOnInit(): void { // Oh my goodness. TODO: Fix this mess if (this.reverse) { this.reverse = this.reverse.reverse(); } } repositionLines() { this.lines.forEach(line => line.position()); } ngAfterViewInit() { const itemArray = this.items.toArray(); for (let i = 0; i < itemArray.length - 1; i++) { const fromItem = itemArray[i].nativeElement; const toItem = itemArray[i + 1].nativeElement; const colorScale = d3.scaleLinear() .domain([90, 75, 60, 30, 0]) .range([gradeColors.bad, gradeColors.poor, gradeColors.fair, gradeColors.good, gradeColors.excellent]); // Drawn backwards to get label on outside let startAnchor = LeaderLine.pointAnchor(toItem, { x: 0, y: '30%' }); let endAnchor = LeaderLine.pointAnchor(fromItem, { x: 0, y: '70%' }); const forward = new LeaderLine(startAnchor, endAnchor, { ...this.lineOptions, color: colorScale(this.forward[i + 1].Delay), middleLabel: LeaderLine.pathLabel(`${this.forward[i + 1].Delay} ms`, this.labelOptions), }); this.lines.push(forward); // If reverse data is available draw return sequence line if (this.reverse) { startAnchor = LeaderLine.pointAnchor(fromItem, { x: '100%', y: '70%' }); endAnchor = LeaderLine.pointAnchor(toItem, { x: '100%', y: '30%' }); const reverse = new LeaderLine(startAnchor, endAnchor, { ...this.lineOptions, color: colorScale(this.reverse[i].Delay), middleLabel: LeaderLine.pathLabel(`${this.reverse[i].Delay} ms`, this.labelOptions), }); this.lines.push(reverse) } } this.initWrapper(); } initWrapper(): void { const lineElements = document.querySelectorAll('.leader-line'); const containerElement = this.lineContainer.nativeElement; // Spacer const spacerElement = this.lineSpacer.nativeElement; const rectWrapperSpacer = spacerElement.getBoundingClientRect(); spacerElement.style.transform = 'none'; spacerElement.style.transform = 'translate(' + (-(rectWrapperSpacer.left + pageXOffset)) + 'px, ' + (-(rectWrapperSpacer.top + pageYOffset)) + 'px)'; // Actual Container containerElement.style.transform = 'none'; const rectWrapper = containerElement.getBoundingClientRect(); // Move to the origin of coordinates as the document containerElement.style.transform = 'translate(' + (-(rectWrapper.left + pageXOffset)) + 'px, ' + (-(rectWrapper.top + pageYOffset)) + 'px)'; lineElements.forEach(lineElement => { containerElement.append(lineElement); }) } ngOnDestroy(): void { // this.lines.forEach(line => { // line.remove(); // }) const lineElements = document.querySelectorAll('.leader-line'); lineElements.forEach(line => { line.remove(); }) } }