src/app/modules/results-browser/donor-card/donor-card.component.ts
Donor card component which displays data from a patient
changeDetection | ChangeDetectionStrategy.OnPush |
selector | ccf-donor-card |
styleUrls | ./donor-card.component.scss |
templateUrl | ./donor-card.component.html |
Properties |
|
Methods |
Inputs |
Outputs |
HostBindings |
constructor(ga: GoogleAnalyticsService)
|
||||||||
Creates an instance of donor card component.
Parameters :
|
color | |
Type : string
|
|
Allows color of the checkbox background to be set from outside the component |
expanded | |
Type : boolean
|
|
Default value : false
|
|
Allows the expanded state of the card to be set from outside the component |
highlighted | |
Type : boolean
|
|
Default value : false
|
|
selected | |
Type : boolean
|
|
Default value : false
|
|
Allows the selected state to be set from outside the component |
tissueBlock | |
Type : TissueBlockResult
|
|
Tissue Block to generate the donor card from |
checked | |
Type : EventEmitter
|
|
Emits the new checked state whenever it changes |
linkClick | |
Type : EventEmitter
|
|
Emit the url of any link when clicked. |
class |
Type : "ccf-donor-card"
|
Default value : 'ccf-donor-card'
|
HTML Class Name |
handleCheckbox |
handleCheckbox()
|
Handles the logic that needs to run when the checkbox is clicked on.
Returns :
void
|
linkHandler | ||||||||
linkHandler(url: string)
|
||||||||
Handles what happens when an info card is clicked. Passes up the link click event unless the card isn't selected In which case it selects it for ease of use.
Parameters :
Returns :
void
|
toggleExpansion |
toggleExpansion()
|
Ensures that the expanded variable is only changed if selected first.
Returns :
void
|
Readonly clsName |
Type : string
|
Default value : 'ccf-donor-card'
|
Decorators :
@HostBinding('class')
|
HTML Class Name |
hoverState |
Type : string
|
Default value : ''
|
To keep track of which element, if any, are hovered over. |
import { ChangeDetectionStrategy, Component, EventEmitter, HostBinding, Input, Output } from '@angular/core';
import { TissueBlockResult } from 'ccf-database';
import { GoogleAnalyticsService } from 'ngx-google-analytics';
/**
* Donor card component which displays data from a patient
*/
@Component({
selector: 'ccf-donor-card',
templateUrl: './donor-card.component.html',
styleUrls: ['./donor-card.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DonorCardComponent {
/** HTML Class Name */
@HostBinding('class') readonly clsName = 'ccf-donor-card';
/** Tissue Block to generate the donor card from */
@Input() tissueBlock!: TissueBlockResult;
/** Allows the selected state to be set from outside the component */
@Input() selected = false;
/** Allows color of the checkbox background to be set from outside the component */
@Input() color!: string;
/** Allows the expanded state of the card to be set from outside the component */
@Input() expanded = false;
@Input() highlighted = false;
/** Emits the new checked state whenever it changes */
@Output() readonly checked = new EventEmitter<boolean>();
/** Emit the url of any link when clicked. */
@Output() readonly linkClick = new EventEmitter<string>();
/** To keep track of which element, if any, are hovered over. */
hoverState = '';
/**
* Creates an instance of donor card component.
*
* @param ga Analytics service
*/
constructor(private readonly ga: GoogleAnalyticsService) {}
/**
* Handles the logic that needs to run when the checkbox is clicked on.
*/
handleCheckbox(): void {
this.selected = !this.selected;
this.ga.event('selected_toggled', 'donor_card', this.tissueBlock.label, +this.selected);
this.checked.emit(this.selected);
this.expanded = false;
}
/**
* Ensures that the expanded variable is only changed if selected first.
*/
toggleExpansion(): void {
if (this.selected) {
this.expanded = !this.expanded;
this.ga.event('expanded_toggled', 'donor_card', this.tissueBlock.label, +this.expanded);
}
}
/**
* Handles what happens when an info card is clicked.
* Passes up the link click event unless the card isn't selected
* In which case it selects it for ease of use.
*
* @param url the URL to emit up.
*/
linkHandler(url: string): void {
this.ga.event('link_clicked', 'donor_card', this.tissueBlock.label);
if (this.selected) {
this.linkClick.emit(url);
} else {
this.selected = true;
this.checked.emit(this.selected);
}
}
}
<div class="main-container">
<div
class="checkbox-background"
[class.selected]="selected"
[class.highlighted]="highlighted"
[ngStyle]="{ 'background-color': selected ? color : 'transparent' }"
(click)="handleCheckbox()"
>
<mat-checkbox [checked]="selected" class="checkbox" [class.checkselected]="selected"></mat-checkbox>
</div>
<div
class="hoverable default-padding donor mat-elevation-z2"
[class.hover-enabled]="selected"
[class.expanded]="expanded"
[class.highlighted]="highlighted"
>
<div class="d-flex">
<mat-icon class="icon-size icon-dark mr-5">person</mat-icon>
<div class="donor-info">
<div class="title description">{{ tissueBlock.donor.label }}</div>
<div class="description">{{ tissueBlock.donor.description }}</div>
</div>
</div>
<div class="hover-state mat-elevation-z4" (click)="linkHandler(tissueBlock.donor.link)">
<div class="hover-title">DONOR</div>
<mat-icon class="icon-size hover-icon">open_in_new</mat-icon>
</div>
<mat-icon (click)="toggleExpansion()" class="icon-size ml-5 selectable" *ngIf="selected">{{
expanded ? 'expand_less' : 'expand_more'
}}</mat-icon>
</div>
</div>
<ng-container *ngIf="expanded">
<div class="expanded-view mat-elevation-z2 align-end">
<div class="info-block hover-enabled default-padding">
<mat-icon class="icon-size icon-dark mr-5">bubble_chart</mat-icon>
<div class="text-content">
<div class="description">{{ tissueBlock.label }}</div>
<div class="description">{{ tissueBlock.description }}</div>
</div>
<div class="hover-state mat-elevation-z4" (click)="linkHandler(tissueBlock.link)">
<div class="hover-title">TISSUE BLOCK</div>
<mat-icon class="icon-size hover-icon">open_in_new</mat-icon>
</div>
</div>
<ccf-tissue-section-vis
*ngIf="tissueBlock.sections.length > 1"
[totalTissueSections]="tissueBlock.sectionCount"
[tissueSections]="tissueBlock.sections"
></ccf-tissue-section-vis>
<ng-container *ngIf="tissueBlock.datasets.length > 0">
<ccf-thumbnail-carousel (linkClicked)="linkHandler($event.link)" class="mt-05" [data]="tissueBlock.datasets">
</ccf-thumbnail-carousel>
</ng-container>
</div>
<div class="expanded-view w-80 mat-elevation-z2 align-end" *ngFor="let section of tissueBlock.sections">
<div class="default-padding info-block hover-enabled">
<mat-icon class="icon-size mr-5 icon-light">bubble_chart</mat-icon>
<div class="text-content">
<div class="description">{{ section.label }}</div>
<div class="description">{{ section.description }}</div>
</div>
<div class="hover-state mat-elevation-z4" (click)="linkHandler(section.link)">
<div class="hover-title">TISSUE SECTION</div>
<mat-icon class="icon-size hover-icon">open_in_new</mat-icon>
</div>
</div>
<ng-container *ngIf="section.datasets.length > 0">
<ccf-thumbnail-carousel (linkClicked)="linkHandler($event.link)" class="mt-05" [data]="section.datasets">
</ccf-thumbnail-carousel>
</ng-container>
</div>
</ng-container>
./donor-card.component.scss
:host {
width: 100%;
.main-container {
display: flex;
min-height: 3rem;
width: 100%;
align-items: center;
padding-left: 0.5rem;
}
.checkbox-background {
height: 2rem;
width: 2rem;
border-radius: 50%;
margin-right: 0.5rem;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
&.highlighted {
::ng-deep .mdc-checkbox__ripple {
opacity: 0.04;
}
}
}
.align-end {
margin-left: auto;
}
.info-block {
display: flex;
font-size: 0.75rem;
line-height: 1rem;
width: 100%;
align-items: center;
&:not(:first-child) {
margin-top: 0.5rem;
}
.text-content {
.title {
font-weight: 600;
}
}
}
.donor {
display: flex;
font-size: 0.75rem;
line-height: 1rem;
align-items: center;
flex-grow: 1;
height: 2.5rem;
&:hover,
&.highlighted {
box-shadow:
0px 6px 2px -2px rgba(0, 0, 0, 0.2),
0px 4px 4px 0px rgba(0, 0, 0, 0.14),
0px 2px 10px 0px rgba(0, 0, 0, 0.12);
}
.hover-state {
width: 19.5rem !important;
}
.donor-info {
.title {
font-weight: 600;
}
}
}
.hoverable {
position: relative;
}
.hover-enabled {
position: relative;
&:hover {
.hover-state {
opacity: 1;
}
}
}
.hover-state {
transition: opacity 0.15s ease-in-out;
opacity: 0;
cursor: pointer;
position: absolute;
height: 100%;
width: 100%;
top: 0;
margin-left: -0.5rem;
align-items: center;
display: flex;
justify-content: flex-end;
padding-right: 1rem;
font-size: 0.75rem;
.hover-title {
font-weight: lighter;
}
.hover-icon {
margin-left: 1rem;
}
}
.default-padding {
padding: 0.5rem;
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
.expanded-view {
margin-top: 0.25rem;
width: 85%;
margin-bottom: 0.25rem;
}
.d-flex {
display: flex;
align-items: center;
}
.icon-size {
height: 1.5rem;
width: 1.5rem;
font-size: 1.5rem;
}
.selectable {
cursor: pointer;
}
.w-80 {
width: 80% !important;
.info-block {
.text-content {
.description {
width: 16rem !important;
}
}
}
}
.ml-5 {
margin-left: 0.5rem;
}
.mr-5 {
margin-right: 0.5rem;
}
.mt-05 {
margin-top: 0.5rem;
}
.description {
width: 17rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}