import { Component, ChangeDetectionStrategy, input, output, computed } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ButtonComponent, IconComponent, TooltipDirective, SkeletonComponent } from '@sda/base-ui'; import { TlGanttRowComponent } from '../tl-gantt-row'; import { TlTimelineToolbarComponent } from '../tl-timeline-toolbar'; import { GanttService } from '../../services/gantt.service'; import type { TlGanttTask, TlGanttDependency, TlDateTick } from '../../types/gantt.types'; import type { TlGanttTaskClickEvent, TlGanttTaskResizeEvent, TlGanttTaskMoveEvent, TlGanttZoomChangeEvent, TlGanttScrollEvent } from '../../types/event.types'; import { calculateDateRange, sortTasksHierarchically, calculateChartWidth } from '../../utils/gantt.utils'; @Component({ selector: 'tl-gantt-chart', standalone: true, imports: [ CommonModule, ButtonComponent, IconComponent, TooltipDirective, SkeletonComponent, TlGanttRowComponent, TlTimelineToolbarComponent, ], providers: [GanttService], templateUrl: './tl-gantt-chart.component.html', styleUrl: './tl-gantt-chart.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) export class TlGanttChartComponent { constructor(protected readonly ganttService: GanttService) {} readonly tasks = input.required(); readonly dependencies = input([]); readonly zoomLevel = input<'day' | 'week' | 'month' | 'quarter' | 'year'>('week'); readonly rowHeight = input(40); readonly showDependencies = input(true); readonly showProgress = input(true); readonly showTodayLine = input(true); readonly loading = input(false); readonly emptyMessage = input('No tasks'); readonly taskClick = output(); readonly taskResize = output(); readonly taskMove = output(); readonly zoomChange = output(); readonly scroll = output(); protected readonly dateRange = computed(() => { if (this.tasks().length === 0) { return { start: new Date(), end: new Date() }; } return calculateDateRange(this.tasks()); }); protected readonly dateAxis = computed(() => { const range = this.dateRange(); return this.ganttService.calculateDateAxis(range.start, range.end); }); protected readonly sortedTasks = computed(() => { return sortTasksHierarchically(this.tasks()); }); protected readonly taskPositions = computed(() => { const range = this.dateRange(); return this.sortedTasks().map(task => this.ganttService.calculateTaskPosition(task, range.start) ); }); protected readonly todayPosition = computed(() => { if (!this.showTodayLine()) return null; const range = this.dateRange(); return this.ganttService.calculateTodayPosition(range.start); }); protected readonly totalWidth = computed(() => { const range = this.dateRange(); return calculateChartWidth(range.start, range.end, this.ganttService.pixelsPerDay()); }); protected onZoomIn(): void { this.ganttService.zoomIn(); this.zoomChange.emit({ level: this.ganttService.zoomLevel() }); } protected onZoomOut(): void { this.ganttService.zoomOut(); this.zoomChange.emit({ level: this.ganttService.zoomLevel() }); } protected onTaskClick(event: TlGanttTaskClickEvent): void { this.taskClick.emit(event); } protected onTaskResize(event: TlGanttTaskResizeEvent): void { this.taskResize.emit(event); } protected getLoadingItems(): number[] { return Array.from({ length: 5 }, (_, i) => i); } }