File

src/app/issue-detail/timeline/coalesced/timeline-item.component.ts

Description

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.

Implements

AfterViewInit

Metadata

selector app-timeline-item
styleUrls ../timeline.component.scss
templateUrl ./timeline-item.component.html

Index

Properties
Methods
Inputs
Accessors

Constructor

constructor(changeDetector: ChangeDetectorRef)
Parameters :
Name Type Optional
changeDetector ChangeDetectorRef No

Inputs

showDeleted
Type : boolean
Default value : false

If this option is set to true, the template with the appTimelineItemDeleted directive is shown

timelineItem

Set the timeline item to show

Methods

Private updateContents
updateContents()
Returns : void

Properties

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

Accessors

timelineItem
settimelineItem(item: CoalescedTimelineItem | undefined)

Set the timeline item to show

Parameters :
Name Type Optional
item CoalescedTimelineItem | undefined No
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;
  }
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""