File

src/app/shared/components/thumbnail-carousel/thumbnail-carousel.component.ts

Description

Carousel containing sample thumbnails in expanded donor cards

Metadata

Index

Properties
Methods
Inputs
Outputs
HostBindings
Accessors

Constructor

constructor(globalConfig: GlobalConfigState)
Parameters :
Name Type Optional
globalConfig GlobalConfigState<literal type> No

Inputs

data
Type : DatasetResult[]

Items to show in the carousel

Outputs

linkClicked
Type : EventEmitter

Outputs the result whose link was clicked

HostBindings

class
Type : "ccf-thumbnail-carousel"
Default value : 'ccf-thumbnail-carousel'

Primary css class selector

Methods

itemId
itemId(_index: number, item: DatasetResult)

Extract a unique identifier for an item

Parameters :
Name Type Optional Description
_index number No

Unused

item DatasetResult No

The item

Returns : string

An unique identifier

setUrl
setUrl(url: string)
Parameters :
Name Type Optional
url string No
Returns : void
thumbnailUrl
thumbnailUrl(item: DatasetResult)
Parameters :
Name Type Optional
item DatasetResult No
Returns : string

Properties

baseHref
Type : string
Default value : ''
Readonly baseHref$
Default value : this.globalConfig.getOption('baseHref')
Readonly className
Type : string
Default value : 'ccf-thumbnail-carousel'
Decorators :
@HostBinding('class')

Primary css class selector

Readonly config
Type : SwiperOptions
Default value : { allowTouchMove: false, slidesOffsetBefore: 4, slidesOffsetAfter: 4, slidesPerView: 'auto', spaceBetween: 4, watchOverflow: true, }

Swiper configuration

Readonly navigation
Type : NavigationOptions
Default value : { // Normally I would have prefered referencing the elements themselves instead of using selectors // However in this case it does not work with angular swiper prevEl: '#' + this.prevButtonId, nextEl: '#' + this.nextButtonId, }

Navigation configuration

Readonly uid
Default value : nextUid()

Per instance unique identifier

Accessors

prevButtonId
getprevButtonId()

HTML id for previous slide button

Returns : string
nextButtonId
getnextButtonId()

HTML id for next slide button

Returns : string
import { ChangeDetectionStrategy, Component, EventEmitter, HostBinding, Input, Output } from '@angular/core';
import { DatasetResult } from 'ccf-database';
import { GlobalConfigState } from 'ccf-shared';
import { SwiperOptions } from 'swiper';
import { NavigationOptions } from 'swiper/types';

// Returns a unique identifier
const nextUid = (() => {
  let counter = -1;
  return () => {
    counter += 1;
    return counter;
  };
})();

/**
 * Carousel containing sample thumbnails in expanded donor cards
 */
@Component({
  selector: 'ccf-thumbnail-carousel',
  templateUrl: './thumbnail-carousel.component.html',
  styleUrls: ['./thumbnail-carousel.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ThumbnailCarouselComponent {
  /**
   * Primary css class selector
   */
  @HostBinding('class') readonly className = 'ccf-thumbnail-carousel';

  /**
   * Items to show in the carousel
   */
  @Input() data!: DatasetResult[];

  /**
   * Outputs the result whose link was clicked
   */
  @Output() readonly linkClicked = new EventEmitter<DatasetResult>();

  /**
   * Per instance unique identifier
   */
  readonly uid = nextUid();

  /**
   * HTML id for previous slide button
   */
  get prevButtonId(): string {
    return `ccf-thumbnail-carousel-prev-button-${this.uid}`;
  }

  /**
   * HTML id for next slide button
   */
  get nextButtonId(): string {
    return `ccf-thumbnail-carousel-next-button-${this.uid}`;
  }

  /**
   * Swiper configuration
   */
  readonly config: SwiperOptions = {
    allowTouchMove: false,
    slidesOffsetBefore: 4,
    slidesOffsetAfter: 4,
    slidesPerView: 'auto',
    spaceBetween: 4,
    watchOverflow: true,
  };

  /**
   * Navigation configuration
   */
  readonly navigation: NavigationOptions = {
    // Normally I would have prefered referencing the elements themselves instead of using selectors
    // However in this case it does not work with angular swiper
    prevEl: '#' + this.prevButtonId,
    nextEl: '#' + this.nextButtonId,
  };

  readonly baseHref$ = this.globalConfig.getOption('baseHref');

  baseHref = '';

  constructor(private readonly globalConfig: GlobalConfigState<{ baseHref: string }>) {
    this.baseHref$.subscribe((ref) => this.setUrl(ref));
  }

  /**
   * Extract a unique identifier for an item
   *
   * @param _index Unused
   * @param item The item
   * @returns An unique identifier
   */
  itemId(_index: number, item: DatasetResult): string {
    return item.thumbnail;
  }

  setUrl(url: string) {
    this.baseHref = url;
  }

  thumbnailUrl(item: DatasetResult): string {
    return `url(${this.baseHref + item.thumbnail})`;
  }
}
<div class="prev">
  <button mat-icon-button disableRipple [attr.id]="prevButtonId">
    <mat-icon>navigate_before</mat-icon>
  </button>
</div>

<swiper class="swiper" [config]="config" [navigation]="navigation">
  <ng-container *ngFor="let item of data; trackBy: itemId">
    <ng-template swiperSlide>
      <div class="slide">
        <div class="thumbnail" [style.background-image]="thumbnailUrl(item)">
          {{ item.technology }}
        </div>
        <a class="link" (click)="linkClicked.emit(item)">
          DATA
          <mat-icon>open_in_new</mat-icon>
        </a>
      </div>
    </ng-template>
  </ng-container>
</swiper>

<div class="next">
  <button mat-icon-button disableRipple [attr.id]="nextButtonId">
    <!-- Fade element is in here so it can be hidden when the button is disabled -->
    <div class="fade"></div>

    <mat-icon>navigate_next</mat-icon>
  </button>
</div>

./thumbnail-carousel.component.scss

:host {
  display: flex;

  .swiper {
    flex-grow: 1;
    padding-bottom: 0.3125rem;

    ::ng-deep .swiper-slide {
      width: 3.75rem;
      height: 2.8125rem;
    }
  }

  .prev,
  .next {
    display: flex;
    align-items: center;

    height: 2.8125rem;

    button {
      width: 1.5rem;
      height: 1.5rem;
      line-height: 1.5rem;
      padding: 0;
    }

    .fade {
      position: absolute;
      top: -0.625rem;
      left: -2rem;
      z-index: 10;

      width: 2rem;
      height: 2.8125rem + 0.3125rem;

      opacity: 1;
      transition: opacity ease-in-out 0.3s;

      pointer-events: none;
    }

    .swiper-button-disabled,
    .swiper-button-lock {
      cursor: default;
      pointer-events: none;

      .fade {
        opacity: 0;
      }
    }
  }

  .slide {
    position: relative;
    width: 100%;
    height: 100%;

    .thumbnail {
      display: flex;
      align-items: center;
      justify-content: center;
      background-size: cover;

      position: relative;
      width: calc(100% - 2 * 0.125rem);
      height: calc(100% - 2 * 0.125rem);

      border-style: solid;
      border-width: 0.125rem;
      border-radius: 0.25rem;

      background-origin: padding-box;
      background-clip: padding-box;

      font-size: 0.875rem;
      font-weight: bold;

      &:after {
        content: '';

        position: absolute;
        bottom: -0.125rem;
        left: -0.125rem;
        width: 0.125rem;
        height: 0.125rem;
      }
    }

    .link {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;

      position: absolute;
      top: 0;
      bottom: 0;
      right: 0;
      left: 0;

      opacity: 0;
      transition: opacity ease-in-out 0.3s;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.6);

      font-size: 0.75rem;
      line-height: 0.75rem;

      &,
      &:link,
      &:visited,
      &:hover,
      &:focus,
      &:active {
        cursor: pointer;
        text-decoration: none;
      }
    }

    &:hover .link {
      opacity: 1;
    }
  }
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""