src/app/components/two-dim-image/two-dim-image.component.ts
Displays an image and a modal inside a card
selector | ccf-two-dim-image |
styleUrls | ./two-dim-image.component.scss |
templateUrl | ./two-dim-image.component.html |
Methods |
Inputs |
constructor(dialog: MatDialog, downloader: FileDownloadService)
|
|||||||||
Initializes MatDialog and FileDownloadService
Parameters :
|
cardTitle | |
Type : string
|
|
Default value : ''
|
|
Title of the card |
isMultirow | |
Type : boolean
|
|
Default value : false
|
|
Flag to view tissue details in multiple rows |
tissueData | |
Type : OrganData[]
|
|
Default value : []
|
|
Tissue data to be displayed in the cards |
downloadClick | |||||||||
downloadClick(event: Event, url: string)
|
|||||||||
Downloads PNG and SVG files
Parameters :
Returns :
void
|
openImageViewer | ||||||
openImageViewer(content: TemplateRef<>)
|
||||||
Opens a modal with image when large screen size
Parameters :
Returns :
void
|
import { Component, Input, TemplateRef } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { FileDownloadService } from '../../services/file-download/file-download.service';
import { OrganData } from './two-dim-image';
/** Displays an image and a modal inside a card */
@Component({
selector: 'ccf-two-dim-image',
templateUrl: './two-dim-image.component.html',
styleUrls: ['./two-dim-image.component.scss'],
})
export class TwoDimImageComponent {
/** Title of the card */
@Input() cardTitle = '';
/** Tissue data to be displayed in the cards */
@Input() tissueData: OrganData[] = [];
/** Flag to view tissue details in multiple rows */
@Input() isMultirow = false;
/** Initializes MatDialog and FileDownloadService */
constructor(
private readonly dialog: MatDialog,
private readonly downloader: FileDownloadService,
) {}
/** Opens a modal with image when large screen size */
openImageViewer(content: TemplateRef<unknown>): void {
const fontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
const isSmallScreen = window.innerWidth / fontSize < 63;
if (!isSmallScreen) {
this.dialog.open(content, { panelClass: 'two-dim-image-modal' });
}
}
/** Downloads PNG and SVG files */
downloadClick(event: Event, url: string): void {
event.preventDefault();
this.downloader.download(url);
}
}
<mat-card>
<mat-card-title class="card-title">{{ cardTitle }}</mat-card-title>
<mat-divider></mat-divider>
<span *ngFor="let tissues of tissueData" [class.multirow]="!isMultirow">
<ng-container *ngFor="let tissue of tissues.tissueData; let i = index; even as even; last as last; odd as odd">
<mat-card-actions
[class.split]="!isMultirow && tissues.tissueData?.length !== 1"
[class.no-split]="tissues.tissueData?.length === 1"
>
<div class="units-container" *ngIf="isMultirow; else downloadGlb">
<div class="tissue-name">{{ tissue.name }}</div>
<div class="download-button-container">
<a mat-stroked-button disableRipple="true" href="{{ tissue.url }}">
<span class="material-symbols-outlined">info</span>
Metadata
</a>
<a
*ngIf="tissue.png"
mat-stroked-button
disableRipple="true"
[href]="tissue.png"
(click)="downloadClick($event, tissue.png)"
>
<span class="material-symbols-outlined">download</span>
Download PNG
</a>
<a
*ngIf="tissue.svg"
mat-stroked-button
disableRipple="true"
[href]="tissue.svg"
(click)="downloadClick($event, tissue.svg)"
>
<span class="material-symbols-outlined">download</span>
Download SVG
</a>
<a *ngIf="tissue.ai" [href]="tissue.ai" mat-stroked-button disableRipple="true" download>
<span class="material-symbols-outlined">download</span>
Download AI
</a>
</div>
</div>
<mat-divider></mat-divider>
<ng-template #downloadGlb>
<mat-divider *ngIf="i !== 0 && i !== 1"></mat-divider>
<div class="units-container">
<div class="three-dim-name">{{ tissue.name }}</div>
<div class="filler"></div>
<div class="download-buttons">
<a [href]="tissue.threeDimImage" download mat-stroked-button disableRipple="true">
<span class="material-symbols-outlined">download</span>
Download GLB
</a>
<a mat-stroked-button disableRipple="true" href="{{ tissue.url }}">
<span class="material-symbols-outlined">info</span>
Metadata
</a>
</div>
</div>
</ng-template>
<div class="image-container" *ngIf="tissue.image; else threeDimensionContainer">
<img [ngSrc]="tissue.image" [alt]="tissue.alt" (click)="openImageViewer(imageContent)" priority fill />
<ng-template #imageContent>
<div class="dialog-header">
<div mat-dialog-title>{{ tissue.name }}</div>
<div class="filler"></div>
<button mat-dialog-close mat-button [disableRipple]="true">
<mat-icon>close</mat-icon>
</button>
</div>
<div class="dialog-buttons">
<a mat-dialog-close mat-stroked-button [disableRipple]="true" href="{{ tissue.url }}">
<span class="material-symbols-outlined">info</span>
Metadata
</a>
<a
*ngIf="tissue.png"
mat-stroked-button
disableRipple="true"
[href]="tissue.png"
(click)="downloadClick($event, tissue.png)"
>
<span class="material-symbols-outlined">download</span>
Download PNG
</a>
<a
*ngIf="tissue.svg"
[href]="tissue.svg"
mat-stroked-button
disableRipple="true"
(click)="downloadClick($event, tissue.svg)"
>
<span class="material-symbols-outlined">download</span>
Download SVG
</a>
<a *ngIf="tissue.ai" [href]="tissue.ai" mat-stroked-button disableRipple="true" download>
<span class="material-symbols-outlined">download</span>
Download AI
</a>
</div>
<div class="img-container">
<img [src]="tissue.expandedImage" [alt]="tissue.alt" class="dialog-image" />
</div>
</ng-template>
</div>
<ng-template #threeDimensionContainer>
<div class="model-container">
<model-viewer
class="model-viewer"
alt="{{ tissue.alt }}"
src="{{ tissue.threeDimImage }}"
shadow-intensity="1"
camera-controls
auto-rotate
style="height: 400px"
>
</model-viewer>
</div>
</ng-template>
</mat-card-actions>
<mat-divider [vertical]="!isMultirow" *ngIf="(isMultirow && last) || even || (odd && !last)"></mat-divider>
</ng-container>
</span>
</mat-card>
./two-dim-image.component.scss
:host {
.mat-mdc-card {
margin-bottom: 5rem;
padding: unset;
}
.mdc-card__actions {
flex-direction: column;
align-items: unset;
padding: unset;
}
.card-title {
font-weight: 300;
font-size: 1.5rem;
line-height: 1.5rem;
letter-spacing: 0.005em;
padding: 1.6875rem 2rem;
margin-bottom: unset;
}
.mat-mdc-outlined-button {
padding: 0rem 1.5rem;
margin-left: 1px;
min-width: 11.5rem;
color: #444c65;
border-radius: 6.25rem;
height: 40px;
background-color: #f7f2fa;
box-shadow:
0px 1px 2px rgba(0, 0, 0, 0.3),
0px 1px 3px 1px rgba(0, 0, 0, 0.15);
}
.mat-card-actions .mat-stroked-button {
margin-right: 1rem;
}
a,
button {
float: right;
}
.image-container {
display: flex;
width: 100%;
justify-content: center;
height: 28rem;
position: relative;
}
.multirow {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.split {
width: 49%;
}
.no-split {
width: 100%;
}
.units-container {
padding: 1.688rem 2rem;
font-weight: 300;
font-size: 1.5rem;
line-height: 1.5rem;
letter-spacing: 0.005rem;
color: #212121;
display: flex;
justify-content: space-between;
align-items: center;
}
.model-container {
display: flex;
justify-content: center;
}
a:-webkit-any-link {
text-decoration: unset !important;
}
.filler {
flex-grow: 1;
}
//3d-styles
.mat-mdc-card-actions:not(.mat-mdc-card-actions-align-end) .mat-mdc-outlined-button:first-child {
margin: 0 1rem;
}
.image-container img {
object-fit: contain;
width: 100%;
}
.download-button-container {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
}
.download-button-container .mat-mdc-outlined-button:not(:last-child) {
margin-right: 1rem;
}
@media (max-width: 72rem) {
//3d-styles
.mat-mdc-card-actions:not(.mat-mdc-card-actions-align-end) .mat-mdc-outlined-button:first-child {
margin: 0;
margin-bottom: 1rem;
}
.mat-card-actions .mat-stroked-button {
margin: 0 0.5rem;
margin-bottom: 1rem;
padding: 0;
}
.mat-card-actions:not(.mat-card-actions-align-end) .mat-stroked-button:first-child {
margin: 0 0.5rem 1rem 0.5rem;
}
}
@media (max-width: 46rem) {
.download-button-container .mat-mdc-outlined-button:not(:last-child) {
margin-right: unset;
margin-bottom: 1rem;
width: max-content;
}
.multirow {
flex-direction: column;
}
.split {
width: unset;
}
.download-buttons {
display: flex;
flex-direction: column;
}
.units-container {
flex-direction: row;
align-items: flex-start;
flex-wrap: wrap;
padding: 1rem;
}
.tissue-name {
margin-bottom: 1rem;
font-size: 1.125rem;
}
.card-title {
font-size: 1.125rem;
padding: 1rem;
}
.three-dim-name {
font-size: 1.125rem;
}
.mat-card-actions .mat-divider {
max-width: unset;
width: 100%;
}
.download-button-container {
justify-content: flex-start;
}
}
}
::ng-deep .two-dim-image-modal {
.dialog-header {
display: flex;
flex-direction: row;
width: 100%;
align-items: center;
justify-content: space-between;
}
.mat-mdc-dialog-container .mdc-dialog__surface {
display: flex;
flex-direction: column;
align-items: center;
max-height: 55rem;
min-width: 55rem;
object-fit: contain;
padding: 1.5rem;
}
.mdc-dialog__surface {
overflow-y: unset;
}
.mat-mdc-dialog-title {
--mdc-dialog-subhead-weight: 300;
--mdc-dialog-subhead-size: 1.5rem;
--mdc-dialog-subhead-line-height: 1.5rem;
margin: unset;
margin-right: 0.5rem;
}
.mdc-dialog__title {
padding: 0;
&::before {
content: none;
}
}
.mat-mdc-outlined-button {
display: flex;
align-items: center;
justify-content: center;
font-weight: 500;
font-size: 0.875rem;
padding: 0rem 1.5rem;
margin-left: 1px;
min-width: 10.5rem;
color: #444c65;
border-radius: 6.25rem;
height: 40px;
background-color: #f7f2fa;
box-shadow:
0px 1px 2px rgba(0, 0, 0, 0.3),
0px 1px 3px 1px rgba(0, 0, 0, 0.15);
margin-right: 1rem;
}
.mat-stroked-button:last-child {
margin-right: unset;
}
.mat-stroked-button:not([class*='mat-elevation-z']) {
box-shadow:
0px 1px 2px rgba(0, 0, 0, 0.3),
0px 1px 3px 1px rgba(0, 0, 0, 0.15);
}
.dialog-image {
width: 100%;
height: 100%;
object-fit: contain;
}
.img-container {
max-width: 664px;
height: 792px;
}
a:-webkit-any-link {
text-decoration: unset !important;
}
.dialog-buttons {
display: flex;
padding-top: 1.5rem;
}
.filler {
flex-grow: 1;
}
@media (max-width: 26.75rem) {
.filler {
display: none;
}
}
}
::ng-deep {
.mat-mdc-outlined-button .mdc-button__label {
display: flex;
align-items: center;
letter-spacing: 0;
}
.material-symbols-outlined {
margin-right: 0.5rem;
}
}