import { Component, ChangeDetectionStrategy, input, output, computed, signal } from '@angular/core'; import { TitleCasePipe } from '@angular/common'; import { BadgeComponent, IconComponent, ButtonComponent } from '@sda/base-ui'; import type { TlChangelogVersion, TlChangeCategory } from '../../types/changelog.types'; import type { TlVersionClickEvent, TlChangelogEntryClickEvent } from '../../types/event.types'; import { formatDate } from '../../utils/date.utils'; @Component({ selector: 'tl-changelog-version', standalone: true, imports: [BadgeComponent, IconComponent, ButtonComponent, TitleCasePipe], templateUrl: './tl-changelog-version.component.html', styleUrl: './tl-changelog-version.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, host: { '[class.tl-changelog-version--collapsible]': 'collapsible()', '[class.tl-changelog-version--expanded]': 'isExpanded()', }, }) export class TlChangelogVersionComponent { readonly version = input.required(); readonly collapsible = input(false); readonly expanded = input(true); readonly showCategoryBadges = input(true); readonly showBreakingBadge = input(true); readonly versionClick = output(); readonly entryClick = output(); readonly expandToggle = output(); protected readonly isExpanded = signal(true); protected readonly formattedDate = computed(() => formatDate(this.version().date)); protected readonly entriesByCategory = computed(() => { const entries = this.version().entries; const categories: TlChangeCategory[] = ['added', 'changed', 'fixed', 'removed', 'deprecated', 'security']; return categories .map(category => ({ category, entries: entries.filter(e => e.category === category), })) .filter(group => group.entries.length > 0); }); protected readonly categoryColor = computed(() => { return (category: TlChangeCategory): string => { return `var(--tl-change-${category})`; }; }); protected readonly categoryIcon = computed(() => { return (category: TlChangeCategory): string => { switch (category) { case 'added': return 'plus'; case 'changed': return 'edit'; case 'fixed': return 'check'; case 'removed': return 'minus'; case 'deprecated': return 'alert-triangle'; case 'security': return 'shield'; default: return 'circle'; } }; }); protected onVersionClick(): void { this.versionClick.emit({ version: this.version() }); if (this.collapsible()) { this.toggleExpand(); } } protected toggleExpand(): void { this.isExpanded.update(v => !v); this.expandToggle.emit(this.isExpanded()); } protected onEntryClick(entry: any): void { this.entryClick.emit({ entry, version: this.version() }); } }