File

src/app/components/two-dim-image/two-dim-image.component.ts

Description

Displays an image and a modal inside a card

Metadata

Index

Methods
Inputs

Constructor

constructor(dialog: MatDialog, downloader: FileDownloadService)

Initializes MatDialog and FileDownloadService

Parameters :
Name Type Optional
dialog MatDialog No
downloader FileDownloadService No

Inputs

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

Methods

downloadClick
downloadClick(event: Event, url: string)

Downloads PNG and SVG files

Parameters :
Name Type Optional
event Event No
url string No
Returns : void
openImageViewer
openImageViewer(content: TemplateRef<>)

Opens a modal with image when large screen size

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

results matching ""

    No results matching ""