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>
103 lines
3.6 KiB
TypeScript
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);
|
|
}
|
|
}
|