feat: implement content card component with dynamic features and styles

This commit is contained in:
4gray committed 2026-02-04 22:12:51 +01:00
1 parent d64283e33d
commit e6923d3fff
4 files changed
+260

No files matched your search

@@ -0,0 +1,48 @@
<div class="content-card" (click)="onCardClick()">
<div class="thumbnail-wrapper">
@if (showRemoveButton()) {
<button
mat-icon-button
class="remove-button"
[matTooltip]="removeTooltip()"
(click)="onRemoveClick($event)"
>
<mat-icon>close</mat-icon>
</button>
}
@if (posterUrl()) {
<img
[src]="posterUrl()"
[alt]="title()"
(error)="onImageError($event)"
class="poster"
/>
} @else if (showPlaceholder()) {
<div class="poster-placeholder">
<mat-icon>{{ getPlaceholderIcon() }}</mat-icon>
</div>
} @else {
<img
src="./assets/images/default-poster.png"
[alt]="title()"
class="poster"
/>
}
@if (type()) {
<span class="type-badge" [class]="type()">
{{ type() }}
</span>
}
</div>
<div class="card-info">
<h3>{{ title() }}</h3>
@if (date()) {
<p class="date-info">
{{ date() | date: 'd MMM, HH:mm' }}
</p>
}
</div>
</div>
@@ -0,0 +1,21 @@
@use '../../styles/content-grid' as grid;
.content-card {
@include grid.content-card;
.thumbnail-wrapper {
@include grid.thumbnail-wrapper;
.remove-button {
@include grid.remove-button;
}
.type-badge {
@include grid.type-badge;
}
}
.card-info {
@include grid.card-info;
}
}
@@ -0,0 +1,73 @@
import { DatePipe } from '@angular/common';
import {
ChangeDetectionStrategy,
Component,
input,
output,
} from '@angular/core';
import { MatIconButton } from '@angular/material/button';
import { MatIcon } from '@angular/material/icon';
import { MatTooltip } from '@angular/material/tooltip';
@Component({
selector: 'app-content-card',
standalone: true,
imports: [DatePipe, MatIcon, MatIconButton, MatTooltip],
templateUrl: './content-card.component.html',
styleUrl: './content-card.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ContentCardComponent {
/** URL of the poster image */
readonly posterUrl = input<string>();
/** Title to display */
readonly title = input.required<string>();
/** Content type (live, movie, series) */
readonly type = input<string>();
/** Optional date to display */
readonly date = input<Date | string>();
/** Whether to show the remove button */
readonly showRemoveButton = input<boolean>(false);
/** Tooltip text for the remove button */
readonly removeTooltip = input<string>('Remove');
/** Whether to show placeholder when no poster */
readonly showPlaceholder = input<boolean>(true);
/** Emitted when the card is clicked */
readonly cardClick = output<void>();
/** Emitted when the remove button is clicked */
readonly remove = output<void>();
/** Get the icon for the placeholder based on type */
getPlaceholderIcon(): string {
switch (this.type()) {
case 'live':
return 'live_tv';
case 'series':
return 'tv';
default:
return 'movie';
}
}
onCardClick(): void {
this.cardClick.emit();
}
onRemoveClick(event: Event): void {
event.stopPropagation();
this.remove.emit();
}
onImageError(event: Event): void {
(event.target as HTMLImageElement).src =
'./assets/images/default-poster.png';
}
}