src/app/issue-detail/timeline/coalesced/timeline-item.component.ts
This component provides an easy method to switch between the different possible content types of a timeline item, such as single, coalesced or deleted. Additionally, the formatted time is shown.
selector | app-timeline-item |
styleUrls | ../timeline.component.scss |
templateUrl | ./timeline-item.component.html |
Properties |
Methods |
|
Inputs |
Accessors |
constructor(changeDetector: ChangeDetectorRef)
|
||||||
Parameters :
|
showDeleted | |
Type : boolean
|
|
Default value : false
|
|
If this option is set to true, the template with the |
timelineItem | |
Set the timeline item to show |
Private updateContents |
updateContents()
|
Returns :
void
|
activeItemContent |
Type : TemplateRef<>
|
Default value : null
|
item |
Type : CoalescedTimelineItem
|
timeFormatter |
Type : TimeFormatter
|
Default value : new TimeFormatter()
|
timelineItemContent |
Type : TimelineSingleItemDirective
|
Decorators :
@ContentChild(TimelineSingleItemDirective)
|
The content to be shown if the timeline item was not coalesced |
timelineItemDeletedContent |
Type : TimelineItemDeletedDirective
|
Decorators :
@ContentChild(TimelineItemDeletedDirective)
|
The content to be shown if the timeline item contains deleted data |
timelineItemsContent |
Type : TimelineCoalescedItemsDirective
|
Decorators :
@ContentChild(TimelineCoalescedItemsDirective)
|
The content to be shown if the timeline item was coalesced |
timelineItem | ||||||
settimelineItem(item: CoalescedTimelineItem | undefined)
|
||||||
Set the timeline item to show
Parameters :
Returns :
void
|
import {AfterViewInit, ChangeDetectorRef, Component, ContentChild, Directive, Input, TemplateRef} from '@angular/core';
import {CoalescedTimelineItem} from '@app/issue-detail/timeline/timeline.component';
import {TimeFormatter} from '@app/issue-detail/time-formatter';
/**
* This directive is used in conjunction with an `ng-template`.
* The content of the `ng-template` specifies the content of a timeline item, if it was *not* coalesced
*/
@Directive({
selector: '[appSingleTimelineItem]'
})
export class TimelineSingleItemDirective {
constructor(public template: TemplateRef<unknown>) {}
}
/**
* This directive is used in conjunction with an `ng-template`.
* The content of the `ng-template` specifies the content of a timeline item, if it was coalesced
*/
@Directive({
selector: '[appCoalescedTimelineItems]'
})
export class TimelineCoalescedItemsDirective {
constructor(public template: TemplateRef<unknown>) {}
}
/**
* This directive is used in conjunction with an `ng-template`.
* The content of the `ng-template` specifies the content of a timeline item, if the attribute `showDeleted` of the
* {@link TimelineItemComponent} is set to `true`.
*/
@Directive({
selector: '[appTimelineItemDeleted]'
})
export class TimelineItemDeletedDirective {
constructor(public template: TemplateRef<unknown>) {}
}
/**
* This component provides an easy method to switch between the different possible content types of a timeline item,
* such as single, coalesced or deleted.
* Additionally, the formatted time is shown.
*/
@Component({
selector: 'app-timeline-item',
templateUrl: './timeline-item.component.html',
styleUrls: ['../timeline.component.scss']
})
export class TimelineItemComponent implements AfterViewInit {
/** Set the timeline item to show */
@Input() set timelineItem(item: CoalescedTimelineItem | undefined) {
this.item = item;
this.updateContents();
}
/** If this option is set to true, the template with the `appTimelineItemDeleted` directive is shown */
@Input() showDeleted = false;
/** The content to be shown if the timeline item was not coalesced */
@ContentChild(TimelineSingleItemDirective)
timelineItemContent: TimelineSingleItemDirective;
/** The content to be shown if the timeline item was coalesced */
@ContentChild(TimelineCoalescedItemsDirective)
timelineItemsContent: TimelineCoalescedItemsDirective;
/** The content to be shown if the timeline item contains deleted data */
@ContentChild(TimelineItemDeletedDirective)
timelineItemDeletedContent: TimelineItemDeletedDirective;
timeFormatter: TimeFormatter = new TimeFormatter();
activeItemContent: TemplateRef<unknown> = null;
item: CoalescedTimelineItem;
constructor(private changeDetector: ChangeDetectorRef) {}
ngAfterViewInit() {
this.updateContents();
}
private updateContents() {
if (!this.item) {
return;
}
if (this.showDeleted) {
this.activeItemContent = this.timelineItemDeletedContent.template;
} else if (this.item.isCoalesced && this.timelineItemsContent) {
this.activeItemContent = this.timelineItemsContent.template;
} else if (!this.item.isCoalesced && this.timelineItemContent) {
this.activeItemContent = this.timelineItemContent.template;
}
this.changeDetector.detectChanges();
}
}
<div class="timeline-panel timeline-body" *ngIf="item">
<ng-container [ngTemplateOutlet]="activeItemContent"></ng-container>
<br />
<small class="text-muted">
<time [dateTime]="item.time" [title]="this.timeFormatter.formatTime(item.time)">
{{ this.timeFormatter.formatTimeDifference(item.time) }}
</time>
</small>
</div>
../timeline.component.scss
.timeline {
list-style: none;
margin: 0;
padding: 20px 0 20px;
position: relative;
&::before {
top: 0;
bottom: 0;
position: absolute;
content: " ";
width: 2px;
background: rgba(0, 0, 0, 0.12);
left: 35px;
}
& > li {
margin-bottom: 20px;
position: relative;
display: flex;
align-items: flex-start;
& > .timeline-badge {
color: #fff;
$size: 50px;
width: $size;
height: $size;
line-height: 50px;
font-size: 2.5em;
text-align: center;
position: relative;
top: 16px;
margin-left: calc(36px - (#{$size} / 2));
margin-right: 12px;
background-color: #999999;
z-index: 100;
border-radius: 50%;
}
& > .commentContainer {
flex: 1;
left: -36px;
z-index: 100;
::ng-deep .timeline-item::before {
display: none; // hide duplicate line
}
}
}
}
.timeline-badge.warning {
background-color: #f0ad4e !important;
}
.timeline-badge.closed {
background-color: #ff0036 !important;
}
.timeline-badge.reopened {
background-color: #00ba39 !important;
}
.timeline-title {
margin-top: 0;
color: inherit;
}
.fill-width {
width: 100%;
}
.timeline-body > p,
.timeline-body > ul {
margin-bottom: 0;
}
.timeline-body > p + p {
margin-top: 5px;
}
.timeline-panel {
flex: 1;
border: 1px solid #d4d4d4;
border-radius: 2px;
padding: 20px;
position: relative;
-webkit-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175);
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175);
&::before {
position: absolute;
top: 26px;
display: inline-block;
border-top: 15px solid transparent;
border-left: 15px solid #ccc;
border-right: 0 solid #ccc;
border-bottom: 15px solid transparent;
content: " ";
border-left-width: 0;
border-right-width: 15px;
left: -15px;
right: auto;
}
&::after {
position: absolute;
top: 27px;
display: inline-block;
border-top: 14px solid transparent;
border-left: 14px solid #fafafa;
border-right: 0 solid #fafafa;
border-bottom: 14px solid transparent;
content: " ";
border-left-width: 0;
border-right-width: 14px;
left: -14px;
right: auto;
}
}