• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

worktile / ngx-gantt / 30b6f0fd-8be6-4f56-81ac-1a22b8b3608e

17 Aug 2023 08:26AM UTC coverage: 72.137% (+0.6%) from 71.511%
30b6f0fd-8be6-4f56-81ac-1a22b8b3608e

push

circleci

web-flow
Merge pull request #414 from worktile/bumpAngular16

build: bump angular 16 #INFR-9186

342 of 593 branches covered (57.67%)

Branch coverage included in aggregate %.

1403 of 1826 relevant lines covered (76.83%)

862.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.91
/packages/gantt/src/components/links/links.component.ts
1
import {
2
    Component,
3
    OnInit,
4
    Input,
5
    Output,
6
    EventEmitter,
7
    HostBinding,
8
    Inject,
9
    ChangeDetectorRef,
10
    ElementRef,
11
    OnDestroy,
12
    OnChanges,
13
    NgZone
14
} from '@angular/core';
15
import { empty, EMPTY, merge, NEVER, of, Subject, timer } from 'rxjs';
16
import { takeUntil, skip, debounceTime, switchMap, take } from 'rxjs/operators';
17
import { GanttGroupInternal } from '../../class/group';
18
import { GanttItemInternal } from './../../class/item';
19
import { GanttLineClickEvent } from '../../class/event';
20
import { GanttDragContainer } from '../../gantt-drag-container';
21
import { GANTT_UPPER_TOKEN, GanttUpper } from '../../gantt-upper';
22
import { GanttLinkItem, LinkInternal, LinkColors, GanttLinkType } from '../../class/link';
23
import { GanttLinkLine } from './lines/line';
24
import { createLineGenerator } from './lines/factory';
25

26
@Component({
27
    selector: 'gantt-links-overlay',
28
    templateUrl: './links.component.html'
29
})
30
export class GanttLinksComponent implements OnInit, OnChanges, OnDestroy {
1✔
31
    // @Input() groups: GanttGroupInternal[] = [];
32

33
    // @Input() items: GanttItemInternal[] = [];
34

35
    @Input() flatItems: (GanttGroupInternal | GanttItemInternal)[] = [];
56✔
36

37
    @Output() lineClick = new EventEmitter<GanttLineClickEvent>();
56✔
38

39
    public links: LinkInternal[] = [];
56✔
40

41
    public ganttLinkTypes = GanttLinkType;
56✔
42

43
    public showArrow = false;
56✔
44

45
    private linkItems: GanttLinkItem[] = [];
56✔
46

47
    private firstChange = true;
56✔
48

49
    private linkLine: GanttLinkLine;
50

51
    private unsubscribe$ = new Subject<void>();
56✔
52

53
    @HostBinding('class.gantt-links-overlay') ganttLinksOverlay = true;
56✔
54

55
    constructor(
56
        @Inject(GANTT_UPPER_TOKEN) public ganttUpper: GanttUpper,
56✔
57
        private cdr: ChangeDetectorRef,
56✔
58
        private elementRef: ElementRef,
56✔
59
        private ganttDragContainer: GanttDragContainer,
56✔
60
        private ngZone: NgZone
56✔
61
    ) {}
62

63
    ngOnInit() {
64
        this.linkLine = createLineGenerator(this.ganttUpper.linkOptions.lineType, this.ganttUpper);
56✔
65

66
        this.showArrow = this.ganttUpper.linkOptions.showArrow;
56✔
67
        // this.buildLinks();
68
        this.firstChange = false;
56✔
69

70
        this.buildLinks();
56✔
71

72
        this.ganttDragContainer.dragStarted.pipe(takeUntil(this.unsubscribe$)).subscribe(() => {
56✔
73
            this.elementRef.nativeElement.style.visibility = 'hidden';
3✔
74
        });
75

76
        merge(
56✔
77
            this.ganttUpper.viewChange,
78
            this.ganttUpper.expandChange,
79
            this.ganttUpper.view.start$,
80
            this.ganttUpper.dragEnded,
81
            this.ganttUpper.linkDragEnded,
82
            this.ngZone.onStable.pipe(take(1)).pipe(switchMap(() => this.ganttUpper.table?.dragDropped || EMPTY))
56!
83
        )
84
            .pipe(skip(1), debounceTime(0), takeUntil(this.unsubscribe$))
85
            .subscribe(() => {
86
                this.elementRef.nativeElement.style.visibility = 'visible';
11✔
87
                this.buildLinks();
11✔
88
                this.cdr.detectChanges();
11✔
89
            });
90
    }
91

92
    ngOnChanges() {
93
        if (!this.firstChange) {
122✔
94
            this.buildLinks();
66✔
95
        }
96
    }
97

98
    private computeItemPosition() {
99
        const lineHeight = this.ganttUpper.styles.lineHeight;
133✔
100
        const barHeight = this.ganttUpper.styles.barHeight;
133✔
101
        this.linkItems = [];
133✔
102
        // if (this.groups.length > 0) {
103
        //     let itemNum = 0;
104
        //     let groupNum = 0;
105
        //     this.groups.forEach((group) => {
106
        //         groupNum++;
107
        //         if (group.expanded) {
108
        //             const items = recursiveItems(group.items);
109
        //             items.forEach((item, itemIndex) => {
110
        //                 const y = (groupNum + itemNum + itemIndex) * lineHeight + item.refs.y + barHeight / 2;
111
        //                 this.linkItems.push({
112
        //                     ...item,
113
        //                     before: {
114
        //                         x: item.refs.x,
115
        //                         y
116
        //                     },
117
        //                     after: {
118
        //                         x: item.refs.x + item.refs.width,
119
        //                         y
120
        //                     }
121
        //                 });
122
        //             });
123
        //             itemNum += items.length;
124
        //         }
125
        //     });
126
        // } else {
127
        //     const items = recursiveItems(this.items);
128
        //     items.forEach((item, itemIndex) => {
129
        //         const y = itemIndex * lineHeight + item.refs.y + barHeight / 2;
130
        //         this.linkItems.push({
131
        //             ...item,
132
        //             before: {
133
        //                 x: item.refs.x,
134
        //                 y
135
        //             },
136
        //             after: {
137
        //                 x: item.refs.x + item.refs.width,
138
        //                 y
139
        //             }
140
        //         });
141
        //     });
142
        // }
143

144
        this.flatItems.forEach((item, itemIndex) => {
133✔
145
            if (!item.hasOwnProperty('items')) {
2,137✔
146
                const ganttItem = item as GanttItemInternal;
1,895✔
147
                if (ganttItem.refs) {
1,895✔
148
                    const y = itemIndex * lineHeight + ganttItem.refs.y + barHeight / 2;
992✔
149
                    this.linkItems.push({
992✔
150
                        ...ganttItem,
151
                        before: {
152
                            x: ganttItem.refs.x,
153
                            y
154
                        },
155
                        after: {
156
                            x: ganttItem.refs.x + ganttItem.refs.width,
157
                            y
158
                        }
159
                    });
160
                }
161
            }
162
        });
163
    }
164

165
    buildLinks() {
166
        this.computeItemPosition();
133✔
167
        this.links = [];
133✔
168
        this.linkItems.forEach((source) => {
133✔
169
            if (source.origin.start || source.origin.end) {
992✔
170
                source.links.forEach((link) => {
879✔
171
                    const target = this.linkItems.find((item) => item.id === link.link);
2,109✔
172
                    if (target && (target.origin.start || target.origin.end)) {
248!
173
                        let defaultColor: string = LinkColors.default;
248✔
174
                        let activeColor: string = LinkColors.active;
248✔
175

176
                        if (link.type === GanttLinkType.fs && source.end.getTime() > target.start.getTime()) {
248✔
177
                            defaultColor = LinkColors.blocked;
134✔
178
                            activeColor = LinkColors.blocked;
134✔
179
                        }
180
                        if (link.color) {
248!
181
                            if (typeof link.color === 'string') {
×
182
                                defaultColor = link.color;
×
183
                                activeColor = link.color;
×
184
                            } else {
185
                                defaultColor = link.color.default;
×
186
                                activeColor = link.color.active;
×
187
                            }
188
                        }
189

190
                        this.links.push({
248✔
191
                            path: this.linkLine.generatePath(source, target, link.type),
192
                            source: source.origin,
193
                            target: target.origin,
194
                            type: link.type,
195
                            color: defaultColor,
196
                            defaultColor,
197
                            activeColor
198
                        });
199
                    }
200
                });
201
            }
202
        });
203
    }
204

205
    trackBy(index: number) {
206
        return index;
352✔
207
    }
208

209
    onLineClick(event: MouseEvent, link: LinkInternal) {
210
        this.lineClick.emit({
×
211
            event,
212
            source: link.source,
213
            target: link.target
214
        });
215
    }
216

217
    mouseEnterPath(link: LinkInternal, index: number) {
218
        link.color = link.activeColor || link.defaultColor;
×
219
        if (index < this.links.length - 1) {
×
220
            this.links.splice(index, 1);
×
221
            this.links.push(link);
×
222
        }
223
    }
224

225
    mouseLeavePath(link: LinkInternal) {
226
        link.color = link.defaultColor;
×
227
    }
228

229
    ngOnDestroy() {
230
        this.unsubscribe$.next();
55✔
231
        this.unsubscribe$.complete();
55✔
232
    }
233
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc