src/app/shared/components/thumbnail-carousel/thumbnail-carousel.component.ts
Carousel containing sample thumbnails in expanded donor cards
changeDetection | ChangeDetectionStrategy.OnPush |
selector | ccf-thumbnail-carousel |
styleUrls | ./thumbnail-carousel.component.scss |
templateUrl | ./thumbnail-carousel.component.html |
Properties |
Methods |
Inputs |
Outputs |
HostBindings |
Accessors |
constructor(globalConfig: GlobalConfigState
|
||||||
Parameters :
|
data | |
Type : DatasetResult[]
|
|
Items to show in the carousel |
linkClicked | |
Type : EventEmitter
|
|
Outputs the result whose link was clicked |
class |
Type : "ccf-thumbnail-carousel"
|
Default value : 'ccf-thumbnail-carousel'
|
Primary css class selector |
itemId | ||||||||||||
itemId(_index: number, item: DatasetResult)
|
||||||||||||
Extract a unique identifier for an item
Parameters :
Returns :
string
An unique identifier |
setUrl | ||||||
setUrl(url: string)
|
||||||
Parameters :
Returns :
void
|
thumbnailUrl | ||||||
thumbnailUrl(item: DatasetResult)
|
||||||
Parameters :
Returns :
string
|
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 uid |
Default value : nextUid()
|
Per instance unique identifier |
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;
}
}
}