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

atinc / ngx-tethys / #102

26 May 2026 08:11AM UTC coverage: 91.111% (+0.7%) from 90.407%
#102

push

web-flow
build: bump docgeni to 2.8.0-next.5 (#3809)

4571 of 5491 branches covered (83.25%)

Branch coverage included in aggregate %.

13141 of 13949 relevant lines covered (94.21%)

966.75 hits per line

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

98.8
/src/timeline/timeline.component.ts
1
import {
2
    Component,
3
    OnInit,
4
    ChangeDetectorRef,
5
    ViewEncapsulation,
6
    ChangeDetectionStrategy,
7
    inject,
8
    input,
9
    effect,
10
    computed,
11
    signal,
12
    WritableSignal,
13
    contentChildren
14
} from '@angular/core';
15
import { ThyTimelineItem } from './timeline-item.component';
16
import { ThyTimelineService } from './timeline.service';
17
import { NgTemplateOutlet } from '@angular/common';
18
import { coerceBooleanProperty, ThyBooleanInput } from 'ngx-tethys/util';
19
import { ThyTimeMode } from './timeline.type';
20
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
21

22
export enum ThyTimeModes {
1✔
23
    left = 'left',
1✔
24
    right = 'right',
1✔
25
    center = 'center'
1✔
26
}
27

28
export type ThyTimeDirection = 'horizontal' | 'vertical';
29

30
/**
31
 * 时间轴组件
32
 * @name thy-timeline
33
 * @order 10
34
 */
35
@Component({
36
    changeDetection: ChangeDetectionStrategy.OnPush,
37
    encapsulation: ViewEncapsulation.None,
38
    selector: 'thy-timeline',
39
    providers: [ThyTimelineService],
40
    template: `
41
        <ng-container>
42
            @for (item of timelineItems; track $index) {
43
                <ng-template [ngTemplateOutlet]="item.template()"></ng-template>
44
            }
45
            <ng-template>
46
                <ng-content></ng-content>
47
            </ng-template>
48
        </ng-container>
49
    `,
50
    host: {
51
        class: 'thy-timeline',
52
        '[class.thy-timeline-right]': `rightTimeline()`,
53
        '[class.thy-timeline-center]': `centerTimeline()`,
54
        '[class.thy-timeline-template]': `templateTimeline()`,
55
        '[class.thy-timeline-horizontal]': `horizontal()`
56
    },
57
    imports: [NgTemplateOutlet]
58
})
59
export class ThyTimeline implements OnInit {
1✔
60
    private cdr = inject(ChangeDetectorRef);
9✔
61

62
    private timelineService = inject(ThyTimelineService);
9✔
63

64
    /**
65
     * 节点排序是否倒序
66
     * @default false
67
     */
68
    readonly thyReverse = input<boolean, ThyBooleanInput>(false, { transform: coerceBooleanProperty });
9✔
69

70
    /**
71
     * 改变时间轴和内容的相对位置
72
     * @type left | right | center
73
     * @default left
74
     */
75
    readonly thyMode = input<ThyTimeMode>('left');
9✔
76

77
    /**
78
     * 时间轴的方向
79
     * @type horizontal | vertical
80
     */
81
    readonly thyDirection = input<ThyTimeDirection>('vertical');
9✔
82

83
    public timelineItems: ThyTimelineItem[] = [];
9✔
84

85
    public templateTimeline: WritableSignal<boolean> = signal(false);
9✔
86

87
    public horizontal = computed(() => {
9✔
88
        return this.thyDirection() === 'horizontal' ? true : false;
9✔
89
    });
90

91
    public rightTimeline = computed(() => {
9✔
92
        const thyMode = this.thyMode();
11✔
93
        const horizontal = this.horizontal();
11✔
94
        const templateTimeline = this.templateTimeline();
11✔
95
        if (thyMode && !horizontal) {
11✔
96
            if (thyMode === 'right') {
10✔
97
                return !templateTimeline;
1✔
98
            } else {
99
                return false;
9✔
100
            }
101
        }
102
    });
103

104
    public centerTimeline = computed(() => {
9✔
105
        const thyMode = this.thyMode();
11✔
106
        const horizontal = this.horizontal();
11✔
107
        if (thyMode && !horizontal) {
11✔
108
            return thyMode === 'center';
10✔
109
        }
110
    });
111

112
    readonly listOfItems = contentChildren(ThyTimelineItem);
9✔
113

114
    private takeUntilDestroyed = takeUntilDestroyed();
9✔
115

116
    constructor() {
117
        effect(() => {
9✔
118
            this.updateChildren();
13✔
119
        });
120
    }
121

122
    ngOnInit() {
123
        this.timelineService.check$.pipe(this.takeUntilDestroyed).subscribe(() => {
9✔
124
            this.cdr.markForCheck();
×
125
        });
126
    }
127

128
    private updateChildren(): void {
129
        const listOfItems = this.listOfItems();
13✔
130
        const thyReverse = this.thyReverse();
13✔
131
        if (listOfItems && listOfItems.length) {
13✔
132
            const length = listOfItems.length;
13✔
133
            listOfItems.forEach((item, index) => {
13✔
134
                item.isLast = !thyReverse ? index === length - 1 : index === 0;
39✔
135
                item.isFirst = thyReverse ? index === length - 1 : index === 0;
39✔
136
                item.reverse = thyReverse;
39✔
137
                if (!this.horizontal()) {
39✔
138
                    item.position = getTimelineItemPosition(index, this.thyMode());
37✔
139
                }
140
                if (item.description() || (item.thyPosition() && !this.horizontal())) {
39✔
141
                    this.templateTimeline.set(true);
4✔
142
                }
143
                item.detectChanges();
39✔
144
            });
145
            this.timelineItems = this.thyReverse() ? [...listOfItems].reverse() : [...listOfItems];
13✔
146
        }
147
        this.cdr.markForCheck();
13✔
148
    }
149
}
150

151
function getTimelineItemPosition(index: number, mode: ThyTimeMode): ThyTimeMode | undefined {
152
    return mode === 'left' ? 'left' : mode === 'right' ? 'right' : mode === 'center' && index % 2 === 0 ? 'left' : 'right';
37✔
153
}
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