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

worktile / ngx-gantt / d637c174-91f9-4ea6-a042-50fe450867d1

09 Jan 2026 02:32AM UTC coverage: 73.294% (+0.4%) from 72.862%
d637c174-91f9-4ea6-a042-50fe450867d1

Pull #632

circleci

ggxxgxq
feat: bump gantt package
Pull Request #632: build: bump ng v21

396 of 687 branches covered (57.64%)

Branch coverage included in aggregate %.

4 of 4 new or added lines in 1 file covered. (100.0%)

91 existing lines in 8 files now uncovered.

1720 of 2200 relevant lines covered (78.18%)

1438.25 hits per line

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

61.46
/packages/gantt/src/root.component.ts
1
import { CdkScrollable } from '@angular/cdk/scrolling';
2
import { NgTemplateOutlet } from '@angular/common';
3
import {
4
    afterNextRender,
5
    ChangeDetectorRef,
6
    Component,
7
    contentChild,
8
    ElementRef,
9
    HostListener,
10
    inject,
11
    input,
12
    NgZone,
13
    OnDestroy,
14
    TemplateRef,
15
    viewChild
16
} from '@angular/core';
17
import { outputToObservable } from '@angular/core/rxjs-interop';
18
import { Subject } from 'rxjs';
19
import { startWith, takeUntil } from 'rxjs/operators';
20
import { GanttCalendarGridComponent } from './components/calendar/grid/calendar-grid.component';
21
import { GanttCalendarHeaderComponent } from './components/calendar/header/calendar-header.component';
22
import { GanttDragBackdropComponent } from './components/drag-backdrop/drag-backdrop.component';
23
import { NgxGanttToolbarComponent } from './components/toolbar/toolbar.component';
24
import { GanttSyncScrollXDirective, GanttSyncScrollYDirective } from './directives/sync-scroll.directive';
25
import { GanttDomService, ScrollDirection } from './gantt-dom.service';
26
import { GanttDragContainer } from './gantt-drag-container';
27
import { GanttPrintService } from './gantt-print.service';
28
import { GanttSyncScrollService } from './gantt-sync-scroll.service';
29
import { GANTT_UPPER_TOKEN, GanttUpper } from './gantt-upper';
30
import { GanttDate } from './utils/date';
31
import { passiveListenerOptions } from './utils/passive-listeners';
32

33
@Component({
34
    selector: 'ngx-gantt-root',
35
    templateUrl: './root.component.html',
36
    providers: [GanttDomService, GanttDragContainer, GanttSyncScrollService],
37
    host: {
38
        class: 'gantt'
39
    },
40
    imports: [
41
        CdkScrollable,
42
        NgTemplateOutlet,
43
        GanttCalendarHeaderComponent,
44
        GanttCalendarGridComponent,
45
        GanttDragBackdropComponent,
46
        NgxGanttToolbarComponent,
47
        GanttSyncScrollXDirective,
48
        GanttSyncScrollYDirective
49
    ]
50
})
51
export class NgxGanttRootComponent implements OnDestroy {
1✔
52
    private elementRef = inject<ElementRef<HTMLElement>>(ElementRef);
59✔
53

54
    private ngZone = inject(NgZone);
59✔
55

56
    private dom = inject(GanttDomService);
59✔
57

58
    dragContainer = inject(GanttDragContainer);
59✔
59

60
    ganttUpper = inject<GanttUpper>(GANTT_UPPER_TOKEN);
59✔
61

62
    private printService = inject(GanttPrintService, { optional: true })!;
59✔
63

64
    private cdr = inject(ChangeDetectorRef);
59✔
65

66
    readonly sideWidth = input<number>(undefined);
59✔
67

68
    readonly sideTemplate = contentChild<TemplateRef<any>>('sideTemplate');
59✔
69

70
    readonly mainTemplate = contentChild<TemplateRef<any>>('mainTemplate');
59✔
71

72
    /** The native `<gantt-drag-backdrop></gantt-drag-backdrop>` element. */
73
    readonly backdrop = viewChild(GanttDragBackdropComponent, { read: ElementRef });
59✔
74

75
    verticalScrollbarWidth = 0;
59✔
76

77
    horizontalScrollbarHeight = 0;
59✔
78

79
    private unsubscribe$ = new Subject<void>();
59✔
80

81
    private get view() {
82
        return this.ganttUpper.view;
288✔
83
    }
84

85
    @HostListener('window:resize')
86
    onWindowResize() {
87
        this.computeScrollBarOffset();
×
88
    }
89

90
    constructor() {
91
        const dragContainer = this.dragContainer;
59✔
92
        this.ganttUpper.dragContainer = dragContainer;
59✔
93

94
        afterNextRender(() => {
59✔
95
            this.ngZone.runOutsideAngular(() => {
59✔
96
                this.dom.initialize(this.elementRef);
59✔
97

98
                if (this.printService) {
59✔
99
                    this.printService.register(this.elementRef);
19✔
100
                }
101
                this.setupScrollClass();
59✔
102
                this.setupResize();
59✔
103
                this.setupViewScroll();
59✔
104
                // 优化初始化时Scroll滚动体验问题,通过透明度解决,默认透明度为0,滚动结束后恢复
105
                this.elementRef.nativeElement.style.opacity = '1';
59✔
106
                outputToObservable(this.ganttUpper.viewChange)
59✔
107
                    .pipe(startWith<null, null>(null), takeUntil(this.unsubscribe$))
108
                    .subscribe(() => {
109
                        this.scrollToToday();
60✔
110
                    });
111
                this.computeScrollBarOffset();
59✔
112
            });
113
        });
114
    }
115

116
    computeScrollBarOffset() {
117
        const ganttMainContainer = this.dom.mainContainer as HTMLElement;
59✔
118
        const ganttVerticalScrollContainer = this.dom.verticalScrollContainer as HTMLElement;
59✔
119
        let verticalScrollbarWidth = 0;
59✔
120
        if (ganttVerticalScrollContainer) {
59!
121
            verticalScrollbarWidth = ganttVerticalScrollContainer.offsetWidth - ganttVerticalScrollContainer.clientWidth;
59✔
122
        } else {
123
            verticalScrollbarWidth = ganttMainContainer?.offsetWidth - ganttMainContainer?.clientWidth;
×
124
        }
125
        const horizontalScrollbarHeight = ganttMainContainer?.offsetHeight - ganttMainContainer?.clientHeight;
59✔
126
        // 只在值真正改变时才触发变更检测,避免无限循环
127
        if (this.verticalScrollbarWidth !== verticalScrollbarWidth || this.horizontalScrollbarHeight !== horizontalScrollbarHeight) {
59!
128
            this.verticalScrollbarWidth = verticalScrollbarWidth;
59✔
129
            this.horizontalScrollbarHeight = horizontalScrollbarHeight;
59✔
130
            this.cdr.markForCheck();
59✔
131
        }
132
    }
133

134
    ngOnDestroy(): void {
135
        this.unsubscribe$.next();
59✔
136
    }
137

138
    private setupViewScroll() {
139
        if (this.ganttUpper.disableLoadOnScroll() && !this.ganttUpper.quickTimeFocus()) {
59✔
140
            return;
59✔
141
        }
142
        this.dom
×
143
            .getViewerScroll(passiveListenerOptions)
144
            .pipe(takeUntil(this.unsubscribe$))
145
            .subscribe((event) => {
146
                if (event.direction === ScrollDirection.LEFT) {
×
147
                    const dates = this.ganttUpper.view.addStartDate();
×
148
                    this.cdr.markForCheck();
×
149
                    if (dates) {
×
150
                        event.target.scrollLeft += this.ganttUpper.view.calculateRangeWidth(dates.start, dates.end);
×
UNCOV
151
                        if (this.ganttUpper.loadOnScroll.observers) {
×
152
                            this.ngZone.run(() =>
×
153
                                this.ganttUpper.loadOnScroll.emit({ start: dates.start.getUnixTime(), end: dates.end.getUnixTime() })
×
154
                            );
155
                        }
156
                    }
157
                }
UNCOV
158
                if (event.direction === ScrollDirection.RIGHT) {
×
UNCOV
159
                    const dates = this.ganttUpper.view.addEndDate();
×
UNCOV
160
                    this.cdr.markForCheck();
×
161
                    if (dates && this.ganttUpper.loadOnScroll.observers) {
×
162
                        this.ngZone.run(() =>
×
163
                            this.ganttUpper.loadOnScroll.emit({ start: dates.start.getUnixTime(), end: dates.end.getUnixTime() })
×
164
                        );
165
                    }
166
                }
167
            });
168
    }
169

170
    private setupResize() {
171
        this.dom
59✔
172
            .getResize()
173
            .pipe(takeUntil(this.unsubscribe$))
174
            .subscribe(() => {
UNCOV
175
                this.setupScrollClass();
×
176
            });
177
    }
178

179
    private setupScrollClass() {
180
        const mainContainer = this.dom.mainContainer as HTMLElement;
59✔
181
        const height = mainContainer.offsetHeight;
59✔
182
        const scrollHeight = mainContainer.scrollHeight;
59✔
183
        if (scrollHeight > height) {
59!
UNCOV
184
            this.elementRef.nativeElement.className = 'gantt gantt-scroll';
×
185
        } else {
186
            this.elementRef.nativeElement.className = 'gantt';
59✔
187
        }
188
    }
189

190
    public scrollToToday() {
191
        const x = this.view.getNowX();
60✔
192
        this.dom.scrollMainContainer(x);
60✔
193
    }
194

195
    public scrollToDate(date: number | Date | GanttDate) {
196
        let x: number;
UNCOV
197
        if (typeof date === 'number' || date instanceof Date) {
×
UNCOV
198
            x = this.view.getXAtDate(new GanttDate(date));
×
199
        } else {
200
            x = this.view.getXAtDate(date);
×
201
        }
UNCOV
202
        this.dom.scrollMainContainer(x);
×
203
    }
204
}
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

© 2026 Coveralls, Inc