Files
timeline-elements-ui/src/components/tl-gantt-chart/tl-gantt-chart.component.ts
Giuliano Silvestro a07edd485b Initial commit: timeline-elements-ui library
Angular 19 component library for timelines and event tracking:
timeline, Gantt chart, event cards, date markers, connectors,
and custom event templates. Includes signal-based services,
SCSS design tokens with dark mode, and TypeScript type definitions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 21:58:25 +10:00

103 lines
3.6 KiB
TypeScript

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<TlGanttTask[]>();
readonly dependencies = input<TlGanttDependency[]>([]);
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<TlGanttTaskClickEvent>();
readonly taskResize = output<TlGanttTaskResizeEvent>();
readonly taskMove = output<TlGanttTaskMoveEvent>();
readonly zoomChange = output<TlGanttZoomChangeEvent>();
readonly scroll = output<TlGanttScrollEvent>();
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);
}
}