@masterteam/timeline 0.0.8 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import * as i1 from '@angular/common';
|
|
2
2
|
import { CommonModule, NgTemplateOutlet } from '@angular/common';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
|
-
import { inject, TemplateRef, Directive, Input, input, output, Component, signal, computed, ViewChild, model, contentChild, contentChildren } from '@angular/core';
|
|
4
|
+
import { inject, TemplateRef, Directive, Input, input, output, Component, ChangeDetectionStrategy, signal, computed, ViewChild, DestroyRef, model, contentChild, contentChildren } from '@angular/core';
|
|
5
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
6
|
+
import { TranslocoDirective, TranslocoService } from '@jsverse/transloco';
|
|
5
7
|
import { Popover } from 'primeng/popover';
|
|
8
|
+
import { Tooltip } from 'primeng/tooltip';
|
|
6
9
|
import { Icon } from '@masterteam/icons';
|
|
7
10
|
import * as i1$1 from '@angular/forms';
|
|
8
11
|
import { FormsModule } from '@angular/forms';
|
|
@@ -77,11 +80,11 @@ class TimelineDefaultPopover {
|
|
|
77
80
|
// Requests parent popover host to close.
|
|
78
81
|
requestClose = output();
|
|
79
82
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: TimelineDefaultPopover, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
80
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.3", type: TimelineDefaultPopover, isStandalone: true, selector: "mt-timeline-default-popover", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { requestClose: "requestClose" }, ngImport: i0, template: "<!-- Default details view shown inside PrimeNG popover. -->\r\n<div class=\"w-[22.5rem]\">\r\n <div class=\"mb-3 flex items-center justify-between\">\r\n <h4 class=\"text-sm font-semibold\">
|
|
83
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.3", type: TimelineDefaultPopover, isStandalone: true, selector: "mt-timeline-default-popover", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { requestClose: "requestClose" }, ngImport: i0, template: "<!-- Default details view shown inside PrimeNG popover. -->\r\n<div class=\"w-[22.5rem]\" *transloco=\"let t; prefix: 'timeline'\">\r\n <div class=\"mb-3 flex items-center justify-between\">\r\n <h4 class=\"text-sm font-semibold\">{{ t(\"level-details\") }}</h4>\r\n <button\r\n type=\"button\"\r\n class=\"rounded border border-surface px-2 py-1 text-xs\"\r\n (click)=\"requestClose.emit()\"\r\n >\r\n {{ t(\"close\") }}\r\n </button>\r\n </div>\r\n\r\n <div class=\"grid gap-2 text-sm sm:grid-cols-2\">\r\n <div>\r\n <span class=\"font-semibold\">{{ t(\"name\") }}:</span> {{ item().title }}\r\n </div>\r\n <div>\r\n <span class=\"font-semibold\">{{ t(\"level\") }}:</span>\r\n {{ item().levelDescription }}\r\n </div>\r\n <div>\r\n <span class=\"font-semibold\">{{ t(\"owner\") }}:</span> {{ item().owner }}\r\n </div>\r\n <div>\r\n <span class=\"font-semibold\">{{ t(\"status\") }}:</span>\r\n <span [style.color]=\"item().statusColor\">{{ item().statusName }}</span>\r\n </div>\r\n <div>\r\n <span class=\"font-semibold\">{{ t(\"progress\") }}:</span>\r\n {{ item().progressLabel }}\r\n </div>\r\n <div>\r\n <span class=\"font-semibold\">{{ t(\"range\") }}:</span> {{ item().phase }}\r\n </div>\r\n </div>\r\n</div>\r\n", dependencies: [{ kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }] });
|
|
81
84
|
}
|
|
82
85
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: TimelineDefaultPopover, decorators: [{
|
|
83
86
|
type: Component,
|
|
84
|
-
args: [{ selector: 'mt-timeline-default-popover', standalone: true, template: "<!-- Default details view shown inside PrimeNG popover. -->\r\n<div class=\"w-[22.5rem]\">\r\n <div class=\"mb-3 flex items-center justify-between\">\r\n <h4 class=\"text-sm font-semibold\">
|
|
87
|
+
args: [{ selector: 'mt-timeline-default-popover', standalone: true, imports: [TranslocoDirective], template: "<!-- Default details view shown inside PrimeNG popover. -->\r\n<div class=\"w-[22.5rem]\" *transloco=\"let t; prefix: 'timeline'\">\r\n <div class=\"mb-3 flex items-center justify-between\">\r\n <h4 class=\"text-sm font-semibold\">{{ t(\"level-details\") }}</h4>\r\n <button\r\n type=\"button\"\r\n class=\"rounded border border-surface px-2 py-1 text-xs\"\r\n (click)=\"requestClose.emit()\"\r\n >\r\n {{ t(\"close\") }}\r\n </button>\r\n </div>\r\n\r\n <div class=\"grid gap-2 text-sm sm:grid-cols-2\">\r\n <div>\r\n <span class=\"font-semibold\">{{ t(\"name\") }}:</span> {{ item().title }}\r\n </div>\r\n <div>\r\n <span class=\"font-semibold\">{{ t(\"level\") }}:</span>\r\n {{ item().levelDescription }}\r\n </div>\r\n <div>\r\n <span class=\"font-semibold\">{{ t(\"owner\") }}:</span> {{ item().owner }}\r\n </div>\r\n <div>\r\n <span class=\"font-semibold\">{{ t(\"status\") }}:</span>\r\n <span [style.color]=\"item().statusColor\">{{ item().statusName }}</span>\r\n </div>\r\n <div>\r\n <span class=\"font-semibold\">{{ t(\"progress\") }}:</span>\r\n {{ item().progressLabel }}\r\n </div>\r\n <div>\r\n <span class=\"font-semibold\">{{ t(\"range\") }}:</span> {{ item().phase }}\r\n </div>\r\n </div>\r\n</div>\r\n" }]
|
|
85
88
|
}], propDecorators: { item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: true }] }], requestClose: [{ type: i0.Output, args: ["requestClose"] }] } });
|
|
86
89
|
|
|
87
90
|
/**
|
|
@@ -111,6 +114,7 @@ class TimelineGanttHeader {
|
|
|
111
114
|
orderedGanttSegments = input([], ...(ngDevMode ? [{ debugName: "orderedGanttSegments" }] : []));
|
|
112
115
|
ganttCanvasWidth = input(0, ...(ngDevMode ? [{ debugName: "ganttCanvasWidth" }] : []));
|
|
113
116
|
effectiveGanttSegmentWidthPx = input(96, ...(ngDevMode ? [{ debugName: "effectiveGanttSegmentWidthPx" }] : []));
|
|
117
|
+
zoomToFit = input(false, ...(ngDevMode ? [{ debugName: "zoomToFit" }] : []));
|
|
114
118
|
renderColumns = input(true, ...(ngDevMode ? [{ debugName: "renderColumns" }] : []));
|
|
115
119
|
renderTimeline = input(true, ...(ngDevMode ? [{ debugName: "renderTimeline" }] : []));
|
|
116
120
|
// Converts monthly segments from "M N" to calendar abbreviations.
|
|
@@ -123,20 +127,25 @@ class TimelineGanttHeader {
|
|
|
123
127
|
? this.monthLabels[month - 1]
|
|
124
128
|
: segment.title;
|
|
125
129
|
}
|
|
126
|
-
|
|
127
|
-
return
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
shouldRenderSegmentYear() {
|
|
131
|
+
return this.effectiveGanttSegmentWidthPx() >= 84;
|
|
132
|
+
}
|
|
133
|
+
resolveSegmentTitleAttr(segment) {
|
|
134
|
+
if (segment.year === undefined || segment.year === null) {
|
|
135
|
+
return this.resolveSegmentTitle(segment);
|
|
136
|
+
}
|
|
137
|
+
return `${this.resolveSegmentTitle(segment)} ${segment.year}`;
|
|
138
|
+
}
|
|
139
|
+
resolveSegmentTooltip(segment) {
|
|
140
|
+
return this.resolveSegmentTitleAttr(segment);
|
|
132
141
|
}
|
|
133
142
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: TimelineGanttHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
134
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: TimelineGanttHeader, isStandalone: true, selector: "mt-timeline-gantt-header", inputs: { timelineMode: { classPropertyName: "timelineMode", publicName: "timelineMode", isSignal: true, isRequired: false, transformFunction: null }, resolvedColumns: { classPropertyName: "resolvedColumns", publicName: "resolvedColumns", isSignal: true, isRequired: false, transformFunction: null }, orderedGanttSegments: { classPropertyName: "orderedGanttSegments", publicName: "orderedGanttSegments", isSignal: true, isRequired: false, transformFunction: null }, ganttCanvasWidth: { classPropertyName: "ganttCanvasWidth", publicName: "ganttCanvasWidth", isSignal: true, isRequired: false, transformFunction: null }, effectiveGanttSegmentWidthPx: { classPropertyName: "effectiveGanttSegmentWidthPx", publicName: "effectiveGanttSegmentWidthPx", isSignal: true, isRequired: false, transformFunction: null }, renderColumns: { classPropertyName: "renderColumns", publicName: "renderColumns", isSignal: true, isRequired: false, transformFunction: null }, renderTimeline: { classPropertyName: "renderTimeline", publicName: "renderTimeline", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block min-w-0" }, ngImport: i0, template: "<!-- Sticky metadata columns + timeline period header. -->\r\n<div class=\"flex border-b border-surface bg-surface-50\">\r\n <!-- Left sticky columns (title/status/custom columns). -->\r\n @if (renderColumns()) {\r\n @for (column of resolvedColumns(); track $index) {\r\n <div\r\n class=\"shrink-0 border-e border-surface bg-surface-50 p-3 text-xs font-semibold uppercase text-surface-600\"\r\n [style.width.px]=\"column.widthPx\"\r\n [
|
|
143
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: TimelineGanttHeader, isStandalone: true, selector: "mt-timeline-gantt-header", inputs: { timelineMode: { classPropertyName: "timelineMode", publicName: "timelineMode", isSignal: true, isRequired: false, transformFunction: null }, resolvedColumns: { classPropertyName: "resolvedColumns", publicName: "resolvedColumns", isSignal: true, isRequired: false, transformFunction: null }, orderedGanttSegments: { classPropertyName: "orderedGanttSegments", publicName: "orderedGanttSegments", isSignal: true, isRequired: false, transformFunction: null }, ganttCanvasWidth: { classPropertyName: "ganttCanvasWidth", publicName: "ganttCanvasWidth", isSignal: true, isRequired: false, transformFunction: null }, effectiveGanttSegmentWidthPx: { classPropertyName: "effectiveGanttSegmentWidthPx", publicName: "effectiveGanttSegmentWidthPx", isSignal: true, isRequired: false, transformFunction: null }, zoomToFit: { classPropertyName: "zoomToFit", publicName: "zoomToFit", isSignal: true, isRequired: false, transformFunction: null }, renderColumns: { classPropertyName: "renderColumns", publicName: "renderColumns", isSignal: true, isRequired: false, transformFunction: null }, renderTimeline: { classPropertyName: "renderTimeline", publicName: "renderTimeline", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block min-w-0" }, ngImport: i0, template: "<!-- Sticky metadata columns + timeline period header. -->\r\n<div class=\"flex border-b border-surface bg-surface-50\">\r\n <!-- Left sticky columns (title/status/custom columns). -->\r\n @if (renderColumns()) {\r\n @for (column of resolvedColumns(); track $index) {\r\n <div\r\n class=\"shrink-0 border-e border-surface bg-surface-50 p-3 text-xs font-semibold uppercase text-surface-600\"\r\n [style.width.px]=\"column.widthPx\"\r\n [class.text-start]=\"column.position === 'start'\"\r\n [class.text-center]=\"column.position === 'center'\"\r\n [class.text-end]=\"column.position === 'end'\"\r\n >\r\n {{ column.header }}\r\n </div>\r\n }\r\n }\r\n\r\n <!-- Time scale columns (month/quarter/half/year). -->\r\n @if (renderTimeline()) {\r\n <div\r\n class=\"flex shrink-0 items-center bg-surface-50\"\r\n [style.width.px]=\"ganttCanvasWidth()\"\r\n >\r\n @for (column of orderedGanttSegments(); track $index) {\r\n <div\r\n class=\"shrink-0 overflow-hidden border-e border-surface px-2 py-3 text-center text-xs font-semibold uppercase text-surface-600 whitespace-nowrap\"\r\n [style.width.px]=\"effectiveGanttSegmentWidthPx()\"\r\n [pTooltip]=\"resolveSegmentTooltip(column)\"\r\n tooltipPosition=\"top\"\r\n [tooltipDisabled]=\"!zoomToFit()\"\r\n >\r\n {{ resolveSegmentTitle(column) }}\r\n @if (\r\n shouldRenderSegmentYear() &&\r\n column.year !== undefined &&\r\n column.year !== null\r\n ) {\r\n {{ column.year }}\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
135
144
|
}
|
|
136
145
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: TimelineGanttHeader, decorators: [{
|
|
137
146
|
type: Component,
|
|
138
|
-
args: [{ selector: 'mt-timeline-gantt-header', standalone: true, imports: [CommonModule], host: { class: 'block min-w-0' }, template: "<!-- Sticky metadata columns + timeline period header. -->\r\n<div class=\"flex border-b border-surface bg-surface-50\">\r\n <!-- Left sticky columns (title/status/custom columns). -->\r\n @if (renderColumns()) {\r\n @for (column of resolvedColumns(); track $index) {\r\n <div\r\n class=\"shrink-0 border-e border-surface bg-surface-50 p-3 text-xs font-semibold uppercase text-surface-600\"\r\n [style.width.px]=\"column.widthPx\"\r\n [
|
|
139
|
-
}], propDecorators: { timelineMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "timelineMode", required: false }] }], resolvedColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "resolvedColumns", required: false }] }], orderedGanttSegments: [{ type: i0.Input, args: [{ isSignal: true, alias: "orderedGanttSegments", required: false }] }], ganttCanvasWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttCanvasWidth", required: false }] }], effectiveGanttSegmentWidthPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "effectiveGanttSegmentWidthPx", required: false }] }], renderColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "renderColumns", required: false }] }], renderTimeline: [{ type: i0.Input, args: [{ isSignal: true, alias: "renderTimeline", required: false }] }] } });
|
|
147
|
+
args: [{ selector: 'mt-timeline-gantt-header', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, Tooltip], host: { class: 'block min-w-0' }, template: "<!-- Sticky metadata columns + timeline period header. -->\r\n<div class=\"flex border-b border-surface bg-surface-50\">\r\n <!-- Left sticky columns (title/status/custom columns). -->\r\n @if (renderColumns()) {\r\n @for (column of resolvedColumns(); track $index) {\r\n <div\r\n class=\"shrink-0 border-e border-surface bg-surface-50 p-3 text-xs font-semibold uppercase text-surface-600\"\r\n [style.width.px]=\"column.widthPx\"\r\n [class.text-start]=\"column.position === 'start'\"\r\n [class.text-center]=\"column.position === 'center'\"\r\n [class.text-end]=\"column.position === 'end'\"\r\n >\r\n {{ column.header }}\r\n </div>\r\n }\r\n }\r\n\r\n <!-- Time scale columns (month/quarter/half/year). -->\r\n @if (renderTimeline()) {\r\n <div\r\n class=\"flex shrink-0 items-center bg-surface-50\"\r\n [style.width.px]=\"ganttCanvasWidth()\"\r\n >\r\n @for (column of orderedGanttSegments(); track $index) {\r\n <div\r\n class=\"shrink-0 overflow-hidden border-e border-surface px-2 py-3 text-center text-xs font-semibold uppercase text-surface-600 whitespace-nowrap\"\r\n [style.width.px]=\"effectiveGanttSegmentWidthPx()\"\r\n [pTooltip]=\"resolveSegmentTooltip(column)\"\r\n tooltipPosition=\"top\"\r\n [tooltipDisabled]=\"!zoomToFit()\"\r\n >\r\n {{ resolveSegmentTitle(column) }}\r\n @if (\r\n shouldRenderSegmentYear() &&\r\n column.year !== undefined &&\r\n column.year !== null\r\n ) {\r\n {{ column.year }}\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n" }]
|
|
148
|
+
}], propDecorators: { timelineMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "timelineMode", required: false }] }], resolvedColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "resolvedColumns", required: false }] }], orderedGanttSegments: [{ type: i0.Input, args: [{ isSignal: true, alias: "orderedGanttSegments", required: false }] }], ganttCanvasWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttCanvasWidth", required: false }] }], effectiveGanttSegmentWidthPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "effectiveGanttSegmentWidthPx", required: false }] }], zoomToFit: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoomToFit", required: false }] }], renderColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "renderColumns", required: false }] }], renderTimeline: [{ type: i0.Input, args: [{ isSignal: true, alias: "renderTimeline", required: false }] }] } });
|
|
140
149
|
|
|
141
150
|
/**
|
|
142
151
|
* Single-row renderer for the Gantt body.
|
|
@@ -204,13 +213,6 @@ class TimelineGanttRow {
|
|
|
204
213
|
}
|
|
205
214
|
return this.path(item, column.key);
|
|
206
215
|
}
|
|
207
|
-
resolveColumnAlignClass(column) {
|
|
208
|
-
return {
|
|
209
|
-
'justify-start': column.position === 'start',
|
|
210
|
-
'justify-center': column.position === 'center',
|
|
211
|
-
'justify-end': column.position === 'end',
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
216
|
getColumnTemplateContext(column, item) {
|
|
215
217
|
const source = item.source ?? item;
|
|
216
218
|
return {
|
|
@@ -241,6 +243,12 @@ class TimelineGanttRow {
|
|
|
241
243
|
onSelect: (event) => this.onProgressClick(item, event),
|
|
242
244
|
};
|
|
243
245
|
}
|
|
246
|
+
shouldRenderProgressLabel(item) {
|
|
247
|
+
return item.trackWidthPx >= 72;
|
|
248
|
+
}
|
|
249
|
+
resolveProgressFillMinWidthPx(item) {
|
|
250
|
+
return item.trackWidthPx > 0 ? Math.min(54, item.trackWidthPx) : 0;
|
|
251
|
+
}
|
|
244
252
|
// Reads document direction to support sticky offsets and progress alignment.
|
|
245
253
|
isRtl() {
|
|
246
254
|
if (typeof document === 'undefined') {
|
|
@@ -270,11 +278,11 @@ class TimelineGanttRow {
|
|
|
270
278
|
}, item);
|
|
271
279
|
}
|
|
272
280
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: TimelineGanttRow, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
273
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: TimelineGanttRow, isStandalone: true, selector: "mt-timeline-gantt-row", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, resolvedColumns: { classPropertyName: "resolvedColumns", publicName: "resolvedColumns", isSignal: true, isRequired: false, transformFunction: null }, ganttCanvasWidth: { classPropertyName: "ganttCanvasWidth", publicName: "ganttCanvasWidth", isSignal: true, isRequired: false, transformFunction: null }, laneInsetPx: { classPropertyName: "laneInsetPx", publicName: "laneInsetPx", isSignal: true, isRequired: false, transformFunction: null }, renderColumns: { classPropertyName: "renderColumns", publicName: "renderColumns", isSignal: true, isRequired: false, transformFunction: null }, renderTimeline: { classPropertyName: "renderTimeline", publicName: "renderTimeline", isSignal: true, isRequired: false, transformFunction: null }, columnTemplatesByKey: { classPropertyName: "columnTemplatesByKey", publicName: "columnTemplatesByKey", isSignal: true, isRequired: false, transformFunction: null }, progressTemplateDirective: { classPropertyName: "progressTemplateDirective", publicName: "progressTemplateDirective", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggleCollapse: "toggleCollapse", progressClick: "progressClick" }, host: { classAttribute: "block min-w-0" }, ngImport: i0, template: "<!-- One gantt data row: sticky metadata cells + progress lane. -->\r\n<div class=\"relative flex\">\r\n <!-- Left sticky metadata cells. -->\r\n @if (renderColumns()) {\r\n @for (column of resolvedColumns(); track $index) {\r\n <div\r\n class=\"flex h-16 shrink-0 items-center overflow-hidden border-e border-b border-surface bg-content p-3\"\r\n [style.width.px]=\"column.widthPx\"\r\n [
|
|
281
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: TimelineGanttRow, isStandalone: true, selector: "mt-timeline-gantt-row", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, resolvedColumns: { classPropertyName: "resolvedColumns", publicName: "resolvedColumns", isSignal: true, isRequired: false, transformFunction: null }, ganttCanvasWidth: { classPropertyName: "ganttCanvasWidth", publicName: "ganttCanvasWidth", isSignal: true, isRequired: false, transformFunction: null }, laneInsetPx: { classPropertyName: "laneInsetPx", publicName: "laneInsetPx", isSignal: true, isRequired: false, transformFunction: null }, renderColumns: { classPropertyName: "renderColumns", publicName: "renderColumns", isSignal: true, isRequired: false, transformFunction: null }, renderTimeline: { classPropertyName: "renderTimeline", publicName: "renderTimeline", isSignal: true, isRequired: false, transformFunction: null }, columnTemplatesByKey: { classPropertyName: "columnTemplatesByKey", publicName: "columnTemplatesByKey", isSignal: true, isRequired: false, transformFunction: null }, progressTemplateDirective: { classPropertyName: "progressTemplateDirective", publicName: "progressTemplateDirective", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggleCollapse: "toggleCollapse", progressClick: "progressClick" }, host: { classAttribute: "block min-w-0" }, ngImport: i0, template: "<!-- One gantt data row: sticky metadata cells + progress lane. -->\r\n<div class=\"relative flex\">\r\n <!-- Left sticky metadata cells. -->\r\n @if (renderColumns()) {\r\n @for (column of resolvedColumns(); track $index) {\r\n <div\r\n class=\"flex h-16 shrink-0 items-center overflow-hidden border-e border-b border-surface bg-content p-3\"\r\n [style.width.px]=\"column.widthPx\"\r\n [class.justify-start]=\"column.position === 'start'\"\r\n [class.justify-center]=\"column.position === 'center'\"\r\n [class.justify-end]=\"column.position === 'end'\"\r\n >\r\n <!-- Per-column custom template, when provided by consumer. -->\r\n @if (resolveColumnTemplate(column); as template) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"template\"\r\n [ngTemplateOutletContext]=\"getColumnTemplateContext(column, item())\"\r\n ></ng-container>\r\n } @else {\r\n <!-- Built-in fallback renderers by column configuration. -->\r\n @if (column.isTreeColumn) {\r\n <div\r\n class=\"flex min-w-0 items-center gap-2\"\r\n [style.padding-inline-start.px]=\"item().levelDepth * 16\"\r\n >\r\n @if (item().hasChildren) {\r\n <mt-icon\r\n class=\"cursor-pointer\"\r\n (click)=\"onToggleCollapse(item())\"\r\n [icon]=\"collapseIcon(item())\"\r\n ></mt-icon>\r\n } @else {\r\n <span class=\"inline-block w-5\"></span>\r\n }\r\n <span class=\"truncate text-sm font-semibold\">\r\n {{ resolveColumnText(column, item()) }}\r\n </span>\r\n </div>\r\n } @else {\r\n <span class=\"truncate text-sm\">\r\n {{ resolveColumnText(column, item()) }}\r\n </span>\r\n }\r\n }\r\n </div>\r\n }\r\n }\r\n\r\n <!-- Right timeline canvas cell for progress track/fill. -->\r\n @if (renderTimeline()) {\r\n <div\r\n class=\"relative z-0 h-16 shrink-0 overflow-hidden border-b border-surface py-4\"\r\n [style.width.px]=\"ganttCanvasWidth()\"\r\n [style.z-index]=\"1\"\r\n >\r\n <div class=\"h-8\"></div>\r\n <!-- Consumer custom progress template. -->\r\n @if (progressTemplateDirective(); as template) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"template.templateRef\"\r\n [ngTemplateOutletContext]=\"getProgressTemplateContext(item())\"\r\n ></ng-container>\r\n } @else {\r\n <!-- Built-in progress renderer. -->\r\n <div\r\n class=\"absolute top-1/2 h-7 -translate-y-1/2 cursor-pointer overflow-hidden rounded-full\"\r\n [style.left.px]=\"\r\n isRtl() ? null : item().startOffsetPx + laneInsetPx()\r\n \"\r\n [style.right.px]=\"\r\n isRtl() ? item().startOffsetPx + laneInsetPx() : null\r\n \"\r\n [style.width.px]=\"item().trackWidthPx\"\r\n [style.background-color]=\"item().progressTrackColor\"\r\n [style.z-index]=\"1\"\r\n [attr.title]=\"item().progressLabel\"\r\n (click)=\"onProgressClick(item(), $event)\"\r\n >\r\n @if (item().progressFillWidthPx > 0) {\r\n <div\r\n class=\"h-full rounded-full\"\r\n [style.width.px]=\"item().progressFillWidthPx\"\r\n [style.min-width.px]=\"resolveProgressFillMinWidthPx(item())\"\r\n [style.background-color]=\"item().statusColor\"\r\n ></div>\r\n }\r\n\r\n @if (shouldRenderProgressLabel(item())) {\r\n <span\r\n class=\"pointer-events-none absolute top-1/2 z-[1] -translate-y-1/2 rounded-full bg-white px-2 py-0.5 text-[10px] font-semibold text-black shadow-sm\"\r\n [style.left.px]=\"isRtl() ? null : 8\"\r\n [style.right.px]=\"isRtl() ? 8 : null\"\r\n [attr.title]=\"item().progressLabel\"\r\n >\r\n {{ item().progressLabel }}\r\n </span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
274
282
|
}
|
|
275
283
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: TimelineGanttRow, decorators: [{
|
|
276
284
|
type: Component,
|
|
277
|
-
args: [{ selector: 'mt-timeline-gantt-row', standalone: true, imports: [CommonModule, Icon, NgTemplateOutlet], host: { class: 'block min-w-0' }, template: "<!-- One gantt data row: sticky metadata cells + progress lane. -->\r\n<div class=\"relative flex\">\r\n <!-- Left sticky metadata cells. -->\r\n @if (renderColumns()) {\r\n @for (column of resolvedColumns(); track $index) {\r\n <div\r\n class=\"flex h-16 shrink-0 items-center overflow-hidden border-e border-b border-surface bg-content p-3\"\r\n [style.width.px]=\"column.widthPx\"\r\n [
|
|
285
|
+
args: [{ selector: 'mt-timeline-gantt-row', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, Icon, NgTemplateOutlet], host: { class: 'block min-w-0' }, template: "<!-- One gantt data row: sticky metadata cells + progress lane. -->\r\n<div class=\"relative flex\">\r\n <!-- Left sticky metadata cells. -->\r\n @if (renderColumns()) {\r\n @for (column of resolvedColumns(); track $index) {\r\n <div\r\n class=\"flex h-16 shrink-0 items-center overflow-hidden border-e border-b border-surface bg-content p-3\"\r\n [style.width.px]=\"column.widthPx\"\r\n [class.justify-start]=\"column.position === 'start'\"\r\n [class.justify-center]=\"column.position === 'center'\"\r\n [class.justify-end]=\"column.position === 'end'\"\r\n >\r\n <!-- Per-column custom template, when provided by consumer. -->\r\n @if (resolveColumnTemplate(column); as template) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"template\"\r\n [ngTemplateOutletContext]=\"getColumnTemplateContext(column, item())\"\r\n ></ng-container>\r\n } @else {\r\n <!-- Built-in fallback renderers by column configuration. -->\r\n @if (column.isTreeColumn) {\r\n <div\r\n class=\"flex min-w-0 items-center gap-2\"\r\n [style.padding-inline-start.px]=\"item().levelDepth * 16\"\r\n >\r\n @if (item().hasChildren) {\r\n <mt-icon\r\n class=\"cursor-pointer\"\r\n (click)=\"onToggleCollapse(item())\"\r\n [icon]=\"collapseIcon(item())\"\r\n ></mt-icon>\r\n } @else {\r\n <span class=\"inline-block w-5\"></span>\r\n }\r\n <span class=\"truncate text-sm font-semibold\">\r\n {{ resolveColumnText(column, item()) }}\r\n </span>\r\n </div>\r\n } @else {\r\n <span class=\"truncate text-sm\">\r\n {{ resolveColumnText(column, item()) }}\r\n </span>\r\n }\r\n }\r\n </div>\r\n }\r\n }\r\n\r\n <!-- Right timeline canvas cell for progress track/fill. -->\r\n @if (renderTimeline()) {\r\n <div\r\n class=\"relative z-0 h-16 shrink-0 overflow-hidden border-b border-surface py-4\"\r\n [style.width.px]=\"ganttCanvasWidth()\"\r\n [style.z-index]=\"1\"\r\n >\r\n <div class=\"h-8\"></div>\r\n <!-- Consumer custom progress template. -->\r\n @if (progressTemplateDirective(); as template) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"template.templateRef\"\r\n [ngTemplateOutletContext]=\"getProgressTemplateContext(item())\"\r\n ></ng-container>\r\n } @else {\r\n <!-- Built-in progress renderer. -->\r\n <div\r\n class=\"absolute top-1/2 h-7 -translate-y-1/2 cursor-pointer overflow-hidden rounded-full\"\r\n [style.left.px]=\"\r\n isRtl() ? null : item().startOffsetPx + laneInsetPx()\r\n \"\r\n [style.right.px]=\"\r\n isRtl() ? item().startOffsetPx + laneInsetPx() : null\r\n \"\r\n [style.width.px]=\"item().trackWidthPx\"\r\n [style.background-color]=\"item().progressTrackColor\"\r\n [style.z-index]=\"1\"\r\n [attr.title]=\"item().progressLabel\"\r\n (click)=\"onProgressClick(item(), $event)\"\r\n >\r\n @if (item().progressFillWidthPx > 0) {\r\n <div\r\n class=\"h-full rounded-full\"\r\n [style.width.px]=\"item().progressFillWidthPx\"\r\n [style.min-width.px]=\"resolveProgressFillMinWidthPx(item())\"\r\n [style.background-color]=\"item().statusColor\"\r\n ></div>\r\n }\r\n\r\n @if (shouldRenderProgressLabel(item())) {\r\n <span\r\n class=\"pointer-events-none absolute top-1/2 z-[1] -translate-y-1/2 rounded-full bg-white px-2 py-0.5 text-[10px] font-semibold text-black shadow-sm\"\r\n [style.left.px]=\"isRtl() ? null : 8\"\r\n [style.right.px]=\"isRtl() ? 8 : null\"\r\n [attr.title]=\"item().progressLabel\"\r\n >\r\n {{ item().progressLabel }}\r\n </span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n" }]
|
|
278
286
|
}], propDecorators: { item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: true }] }], resolvedColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "resolvedColumns", required: false }] }], ganttCanvasWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttCanvasWidth", required: false }] }], laneInsetPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "laneInsetPx", required: false }] }], renderColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "renderColumns", required: false }] }], renderTimeline: [{ type: i0.Input, args: [{ isSignal: true, alias: "renderTimeline", required: false }] }], columnTemplatesByKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnTemplatesByKey", required: false }] }], progressTemplateDirective: [{ type: i0.Input, args: [{ isSignal: true, alias: "progressTemplateDirective", required: false }] }], toggleCollapse: [{ type: i0.Output, args: ["toggleCollapse"] }], progressClick: [{ type: i0.Output, args: ["progressClick"] }] } });
|
|
279
287
|
|
|
280
288
|
/**
|
|
@@ -299,8 +307,16 @@ class TimelineGantt {
|
|
|
299
307
|
this.ganttScrollViewport = value;
|
|
300
308
|
this.bindResizeObserver();
|
|
301
309
|
}
|
|
310
|
+
columnsHeaderScrollViewport;
|
|
311
|
+
set columnsBodyScrollViewportRef(value) {
|
|
312
|
+
this.columnsBodyScrollViewport = value;
|
|
313
|
+
this.ganttBodyScrollTopPx.set(value?.nativeElement.scrollTop ?? 0);
|
|
314
|
+
}
|
|
315
|
+
timelineHeaderScrollViewport;
|
|
316
|
+
timelineBodyScrollViewport;
|
|
302
317
|
ganttSplitContainer;
|
|
303
318
|
ganttScrollViewport;
|
|
319
|
+
columnsBodyScrollViewport;
|
|
304
320
|
resizeObserver;
|
|
305
321
|
splitResizeObserver;
|
|
306
322
|
// Width of the visible horizontal viewport used for adaptive segment sizing.
|
|
@@ -309,13 +325,18 @@ class TimelineGantt {
|
|
|
309
325
|
splitterWidthPx = 10;
|
|
310
326
|
timelineLaneInsetPx = 12;
|
|
311
327
|
adaptiveSegmentMaxWidthPx = 320;
|
|
312
|
-
|
|
328
|
+
ganttRowHeightPx = 64;
|
|
329
|
+
virtualScrollOverscanRows = 6;
|
|
313
330
|
columnsPaneUserWidthPx = signal(null, ...(ngDevMode ? [{ debugName: "columnsPaneUserWidthPx" }] : []));
|
|
314
331
|
lastExpandedColumnsPaneWidthPx = signal(null, ...(ngDevMode ? [{ debugName: "lastExpandedColumnsPaneWidthPx" }] : []));
|
|
332
|
+
ganttBodyScrollTopPx = signal(0, ...(ngDevMode ? [{ debugName: "ganttBodyScrollTopPx" }] : []));
|
|
315
333
|
isColumnsResizing = signal(false, ...(ngDevMode ? [{ debugName: "isColumnsResizing" }] : []));
|
|
316
334
|
canResizeColumnsPane = computed(() => this.baseResolvedColumns().length > 0, ...(ngDevMode ? [{ debugName: "canResizeColumnsPane" }] : []));
|
|
317
335
|
resizeStartClientX = 0;
|
|
318
336
|
resizeStartColumnsPaneWidthPx = 0;
|
|
337
|
+
isSyncingColumnsScroll = false;
|
|
338
|
+
isSyncingTimelineScroll = false;
|
|
339
|
+
isSyncingVerticalScroll = false;
|
|
319
340
|
timelineMode = input('quarterly', ...(ngDevMode ? [{ debugName: "timelineMode" }] : []));
|
|
320
341
|
columns = input(null, ...(ngDevMode ? [{ debugName: "columns" }] : []));
|
|
321
342
|
ganttTitleColumnLabel = input('Portfolio Name', ...(ngDevMode ? [{ debugName: "ganttTitleColumnLabel" }] : []));
|
|
@@ -327,6 +348,10 @@ class TimelineGantt {
|
|
|
327
348
|
defaultVisibleColumns = input(4, ...(ngDevMode ? [{ debugName: "defaultVisibleColumns" }] : []));
|
|
328
349
|
columnsPaneMinWidthPx = input(0, ...(ngDevMode ? [{ debugName: "columnsPaneMinWidthPx" }] : []));
|
|
329
350
|
columnsPaneMaxWidthPx = input(null, ...(ngDevMode ? [{ debugName: "columnsPaneMaxWidthPx" }] : []));
|
|
351
|
+
enableVirtualScroll = input(true, ...(ngDevMode ? [{ debugName: "enableVirtualScroll" }] : []));
|
|
352
|
+
virtualScrollThreshold = input(120, ...(ngDevMode ? [{ debugName: "virtualScrollThreshold" }] : []));
|
|
353
|
+
virtualScrollBodyHeightPx = input(640, ...(ngDevMode ? [{ debugName: "virtualScrollBodyHeightPx" }] : []));
|
|
354
|
+
zoomToFit = input(false, ...(ngDevMode ? [{ debugName: "zoomToFit" }] : []));
|
|
330
355
|
mappedGanttNodes = input([], ...(ngDevMode ? [{ debugName: "mappedGanttNodes" }] : []));
|
|
331
356
|
orderedGanttSegments = input([], ...(ngDevMode ? [{ debugName: "orderedGanttSegments" }] : []));
|
|
332
357
|
collapsedGanttIds = input(new Set(), ...(ngDevMode ? [{ debugName: "collapsedGanttIds" }] : []));
|
|
@@ -334,6 +359,7 @@ class TimelineGantt {
|
|
|
334
359
|
progressTemplateDirective = input(null, ...(ngDevMode ? [{ debugName: "progressTemplateDirective" }] : []));
|
|
335
360
|
toggleCollapse = output();
|
|
336
361
|
progressClick = output();
|
|
362
|
+
timelineViewportWidthChange = output();
|
|
337
363
|
// Resolves the base columns contract before splitter-specific layout is applied.
|
|
338
364
|
baseResolvedColumns = computed(() => {
|
|
339
365
|
const providedColumns = this.columns();
|
|
@@ -399,9 +425,18 @@ class TimelineGantt {
|
|
|
399
425
|
if (availableWidth <= 0) {
|
|
400
426
|
return baseWidth;
|
|
401
427
|
}
|
|
402
|
-
const candidateWidth =
|
|
403
|
-
|
|
428
|
+
const candidateWidth = availableWidth / segmentCount;
|
|
429
|
+
if (this.zoomToFit()) {
|
|
430
|
+
return Math.max(0, Math.min(candidateWidth, this.adaptiveSegmentMaxWidthPx));
|
|
431
|
+
}
|
|
432
|
+
return Math.max(baseWidth, Math.min(Math.floor(candidateWidth), this.adaptiveSegmentMaxWidthPx));
|
|
404
433
|
}, ...(ngDevMode ? [{ debugName: "effectiveGanttSegmentWidthPx" }] : []));
|
|
434
|
+
laneInsetPx = computed(() => {
|
|
435
|
+
if (!this.zoomToFit()) {
|
|
436
|
+
return this.timelineLaneInsetPx;
|
|
437
|
+
}
|
|
438
|
+
return Math.min(this.timelineLaneInsetPx, Math.max(0, this.effectiveGanttSegmentWidthPx() / 4));
|
|
439
|
+
}, ...(ngDevMode ? [{ debugName: "laneInsetPx" }] : []));
|
|
405
440
|
ganttCanvasWidth = computed(() => this.orderedGanttSegments().length * this.effectiveGanttSegmentWidthPx(), ...(ngDevMode ? [{ debugName: "ganttCanvasWidth" }] : []));
|
|
406
441
|
// Flattens the mapped hierarchy into rows using current collapse state.
|
|
407
442
|
resolvedGanttItems = computed(() => {
|
|
@@ -415,6 +450,36 @@ class TimelineGantt {
|
|
|
415
450
|
this.flattenMapped(this.mappedGanttNodes(), 0, segments, firstSeq, lastSeq, this.collapsedGanttIds(), out);
|
|
416
451
|
return out;
|
|
417
452
|
}, ...(ngDevMode ? [{ debugName: "resolvedGanttItems" }] : []));
|
|
453
|
+
resolvedVirtualScrollBodyHeightPx = computed(() => Math.max(this.ganttRowHeightPx, Math.floor(this.virtualScrollBodyHeightPx())), ...(ngDevMode ? [{ debugName: "resolvedVirtualScrollBodyHeightPx" }] : []));
|
|
454
|
+
isVirtualScrollActive = computed(() => {
|
|
455
|
+
const threshold = Math.max(0, Math.floor(this.virtualScrollThreshold()));
|
|
456
|
+
return (this.enableVirtualScroll() &&
|
|
457
|
+
this.resolvedGanttItems().length >= threshold &&
|
|
458
|
+
this.resolvedVirtualScrollBodyHeightPx() > 0);
|
|
459
|
+
}, ...(ngDevMode ? [{ debugName: "isVirtualScrollActive" }] : []));
|
|
460
|
+
virtualScrollWindow = computed(() => {
|
|
461
|
+
const totalItems = this.resolvedGanttItems().length;
|
|
462
|
+
if (!this.isVirtualScrollActive()) {
|
|
463
|
+
return { startIndex: 0, endIndex: totalItems };
|
|
464
|
+
}
|
|
465
|
+
const visibleRows = Math.max(1, Math.ceil(this.resolvedVirtualScrollBodyHeightPx() / this.ganttRowHeightPx));
|
|
466
|
+
const renderCount = visibleRows + this.virtualScrollOverscanRows * 2;
|
|
467
|
+
const maxStartIndex = Math.max(0, totalItems - renderCount);
|
|
468
|
+
const requestedStartIndex = Math.max(0, Math.floor(this.ganttBodyScrollTopPx() / this.ganttRowHeightPx) -
|
|
469
|
+
this.virtualScrollOverscanRows);
|
|
470
|
+
const startIndex = Math.min(requestedStartIndex, maxStartIndex);
|
|
471
|
+
const endIndex = Math.min(totalItems, startIndex + renderCount);
|
|
472
|
+
return { startIndex, endIndex };
|
|
473
|
+
}, ...(ngDevMode ? [{ debugName: "virtualScrollWindow" }] : []));
|
|
474
|
+
visibleResolvedGanttItems = computed(() => {
|
|
475
|
+
const { startIndex, endIndex } = this.virtualScrollWindow();
|
|
476
|
+
return this.resolvedGanttItems().slice(startIndex, endIndex);
|
|
477
|
+
}, ...(ngDevMode ? [{ debugName: "visibleResolvedGanttItems" }] : []));
|
|
478
|
+
virtualScrollTopSpacerHeightPx = computed(() => this.virtualScrollWindow().startIndex * this.ganttRowHeightPx, ...(ngDevMode ? [{ debugName: "virtualScrollTopSpacerHeightPx" }] : []));
|
|
479
|
+
virtualScrollBottomSpacerHeightPx = computed(() => {
|
|
480
|
+
const { endIndex } = this.virtualScrollWindow();
|
|
481
|
+
return Math.max(0, (this.resolvedGanttItems().length - endIndex) * this.ganttRowHeightPx);
|
|
482
|
+
}, ...(ngDevMode ? [{ debugName: "virtualScrollBottomSpacerHeightPx" }] : []));
|
|
418
483
|
ngAfterViewInit() {
|
|
419
484
|
this.bindResizeObserver();
|
|
420
485
|
}
|
|
@@ -430,6 +495,24 @@ class TimelineGantt {
|
|
|
430
495
|
onRowProgressClick(payload) {
|
|
431
496
|
this.progressClick.emit(payload);
|
|
432
497
|
}
|
|
498
|
+
onColumnsHeaderScroll(event) {
|
|
499
|
+
this.syncColumnsScroll(event.target, this.columnsBodyScrollViewport?.nativeElement);
|
|
500
|
+
}
|
|
501
|
+
onColumnsBodyScroll(event) {
|
|
502
|
+
const target = event.target;
|
|
503
|
+
this.ganttBodyScrollTopPx.set(target?.scrollTop ?? 0);
|
|
504
|
+
this.syncColumnsScroll(target, this.columnsHeaderScrollViewport?.nativeElement);
|
|
505
|
+
this.syncVerticalScroll(target, this.timelineBodyScrollViewport?.nativeElement);
|
|
506
|
+
}
|
|
507
|
+
onTimelineHeaderScroll(event) {
|
|
508
|
+
this.syncTimelineScroll(event.target, this.timelineBodyScrollViewport?.nativeElement);
|
|
509
|
+
}
|
|
510
|
+
onTimelineBodyScroll(event) {
|
|
511
|
+
const target = event.target;
|
|
512
|
+
this.ganttBodyScrollTopPx.set(target?.scrollTop ?? 0);
|
|
513
|
+
this.syncTimelineScroll(target, this.timelineHeaderScrollViewport?.nativeElement);
|
|
514
|
+
this.syncVerticalScroll(target, this.columnsBodyScrollViewport?.nativeElement);
|
|
515
|
+
}
|
|
433
516
|
onSplitterPointerDown(event) {
|
|
434
517
|
if (event.button !== 0 || !this.canResizeColumnsPane()) {
|
|
435
518
|
return;
|
|
@@ -499,6 +582,7 @@ class TimelineGantt {
|
|
|
499
582
|
return;
|
|
500
583
|
}
|
|
501
584
|
this.ganttViewportWidth.set(viewport.clientWidth);
|
|
585
|
+
this.timelineViewportWidthChange.emit(viewport.clientWidth);
|
|
502
586
|
if (typeof ResizeObserver === 'undefined') {
|
|
503
587
|
return;
|
|
504
588
|
}
|
|
@@ -508,6 +592,7 @@ class TimelineGantt {
|
|
|
508
592
|
this.ganttScrollViewport?.nativeElement.clientWidth ??
|
|
509
593
|
0;
|
|
510
594
|
this.ganttViewportWidth.set(width);
|
|
595
|
+
this.timelineViewportWidthChange.emit(width);
|
|
511
596
|
});
|
|
512
597
|
this.resizeObserver.observe(viewport);
|
|
513
598
|
}
|
|
@@ -551,6 +636,45 @@ class TimelineGantt {
|
|
|
551
636
|
window.removeEventListener('pointerup', this.onSplitterPointerUp);
|
|
552
637
|
window.removeEventListener('pointercancel', this.onSplitterPointerUp);
|
|
553
638
|
}
|
|
639
|
+
syncColumnsScroll(source, target) {
|
|
640
|
+
if (!source || !target || this.isSyncingColumnsScroll) {
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
if (target.scrollLeft === source.scrollLeft) {
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
this.isSyncingColumnsScroll = true;
|
|
647
|
+
target.scrollLeft = source.scrollLeft;
|
|
648
|
+
queueMicrotask(() => {
|
|
649
|
+
this.isSyncingColumnsScroll = false;
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
syncTimelineScroll(source, target) {
|
|
653
|
+
if (!source || !target || this.isSyncingTimelineScroll) {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
if (target.scrollLeft === source.scrollLeft) {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
this.isSyncingTimelineScroll = true;
|
|
660
|
+
target.scrollLeft = source.scrollLeft;
|
|
661
|
+
queueMicrotask(() => {
|
|
662
|
+
this.isSyncingTimelineScroll = false;
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
syncVerticalScroll(source, target) {
|
|
666
|
+
if (!source || !target || this.isSyncingVerticalScroll) {
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
if (target.scrollTop === source.scrollTop) {
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
this.isSyncingVerticalScroll = true;
|
|
673
|
+
target.scrollTop = source.scrollTop;
|
|
674
|
+
queueMicrotask(() => {
|
|
675
|
+
this.isSyncingVerticalScroll = false;
|
|
676
|
+
});
|
|
677
|
+
}
|
|
554
678
|
applyColumnsPaneWidth(widthPx) {
|
|
555
679
|
const clampedWidthPx = this.clampColumnsPaneWidth(widthPx);
|
|
556
680
|
this.columnsPaneUserWidthPx.set(clampedWidthPx);
|
|
@@ -616,7 +740,8 @@ class TimelineGantt {
|
|
|
616
740
|
const normalizedEndSeq = Math.min(lastSeq, Math.max(startSeq, endSeq));
|
|
617
741
|
const widthSegments = normalizedEndSeq - normalizedStartSeq + 1;
|
|
618
742
|
const widthPx = Math.max(this.effectiveGanttSegmentWidthPx(), widthSegments * this.effectiveGanttSegmentWidthPx());
|
|
619
|
-
const
|
|
743
|
+
const laneInsetPx = this.laneInsetPx();
|
|
744
|
+
const trackWidthPx = Math.max(0, widthPx - laneInsetPx * 2);
|
|
620
745
|
const progressValue = this.clampProgress(node.progress.value);
|
|
621
746
|
const statusColor = this.resolveBaseColor(node.color ?? node.progress.color ?? null);
|
|
622
747
|
return {
|
|
@@ -720,18 +845,35 @@ class TimelineGantt {
|
|
|
720
845
|
return dir.toLowerCase() === 'rtl';
|
|
721
846
|
}
|
|
722
847
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: TimelineGantt, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
723
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: TimelineGantt, isStandalone: true, selector: "mt-timeline-gantt", inputs: { timelineMode: { classPropertyName: "timelineMode", publicName: "timelineMode", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, ganttTitleColumnLabel: { classPropertyName: "ganttTitleColumnLabel", publicName: "ganttTitleColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, ganttStatusColumnLabel: { classPropertyName: "ganttStatusColumnLabel", publicName: "ganttStatusColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, ganttInitiativeColumnWidthPx: { classPropertyName: "ganttInitiativeColumnWidthPx", publicName: "ganttInitiativeColumnWidthPx", isSignal: true, isRequired: false, transformFunction: null }, ganttStatusColumnWidthPx: { classPropertyName: "ganttStatusColumnWidthPx", publicName: "ganttStatusColumnWidthPx", isSignal: true, isRequired: false, transformFunction: null }, showGanttStatusColumn: { classPropertyName: "showGanttStatusColumn", publicName: "showGanttStatusColumn", isSignal: true, isRequired: false, transformFunction: null }, ganttSegmentWidthPx: { classPropertyName: "ganttSegmentWidthPx", publicName: "ganttSegmentWidthPx", isSignal: true, isRequired: false, transformFunction: null }, defaultVisibleColumns: { classPropertyName: "defaultVisibleColumns", publicName: "defaultVisibleColumns", isSignal: true, isRequired: false, transformFunction: null }, columnsPaneMinWidthPx: { classPropertyName: "columnsPaneMinWidthPx", publicName: "columnsPaneMinWidthPx", isSignal: true, isRequired: false, transformFunction: null }, columnsPaneMaxWidthPx: { classPropertyName: "columnsPaneMaxWidthPx", publicName: "columnsPaneMaxWidthPx", isSignal: true, isRequired: false, transformFunction: null }, mappedGanttNodes: { classPropertyName: "mappedGanttNodes", publicName: "mappedGanttNodes", isSignal: true, isRequired: false, transformFunction: null }, orderedGanttSegments: { classPropertyName: "orderedGanttSegments", publicName: "orderedGanttSegments", isSignal: true, isRequired: false, transformFunction: null }, collapsedGanttIds: { classPropertyName: "collapsedGanttIds", publicName: "collapsedGanttIds", isSignal: true, isRequired: false, transformFunction: null }, columnTemplatesByKey: { classPropertyName: "columnTemplatesByKey", publicName: "columnTemplatesByKey", isSignal: true, isRequired: false, transformFunction: null }, progressTemplateDirective: { classPropertyName: "progressTemplateDirective", publicName: "progressTemplateDirective", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggleCollapse: "toggleCollapse", progressClick: "progressClick" }, viewQueries: [{ propertyName: "ganttSplitContainerRef", first: true, predicate: ["ganttSplitContainer"], descendants: true }, { propertyName: "ganttScrollViewportRef", first: true, predicate: ["ganttScrollViewport"], descendants: true }], ngImport: i0, template: "<!-- Gantt view shell: scroll viewport + sized timeline table. -->\n<div class=\"min-h-56 min-w-0 overflow-hidden py-2\">\n <div\n class=\"flex min-w-0 border border-surface bg-content\"\n #ganttSplitContainer\n >\n <!-- Fixed metadata columns pane. -->\n <div\n class=\"shrink-0 overflow-hidden\"\n [style.width.px]=\"resolvedColumnsPaneWidthPx()\"\n >\n <div class=\"mt-timeline-scroll h-full overflow-x-auto overflow-y-hidden\">\n <div class=\"min-w-full\" [style.width.px]=\"columnsContentWidthPx()\">\n <mt-timeline-gantt-header\n [timelineMode]=\"timelineMode()\"\n [resolvedColumns]=\"resolvedColumns()\"\n [orderedGanttSegments]=\"orderedGanttSegments()\"\n [ganttCanvasWidth]=\"0\"\n [effectiveGanttSegmentWidthPx]=\"effectiveGanttSegmentWidthPx()\"\n [renderTimeline]=\"false\"\n />\n\n <div>\n @for (item of resolvedGanttItems(); track $index) {\n <mt-timeline-gantt-row\n [item]=\"item\"\n [resolvedColumns]=\"resolvedColumns()\"\n [ganttCanvasWidth]=\"0\"\n [laneInsetPx]=\"laneInsetPx\"\n [columnTemplatesByKey]=\"columnTemplatesByKey()\"\n [progressTemplateDirective]=\"progressTemplateDirective()\"\n [renderTimeline]=\"false\"\n (toggleCollapse)=\"onToggleCollapse($event)\"\n (progressClick)=\"onRowProgressClick($event)\"\n />\n }\n </div>\n </div>\n </div>\n </div>\n\n @if (canResizeColumnsPane()) {\n <button\n type=\"button\"\n class=\"mt-timeline-splitter shrink-0\"\n [class.mt-timeline-splitter-active]=\"isColumnsResizing()\"\n (pointerdown)=\"onSplitterPointerDown($event)\"\n (dblclick)=\"onSplitterDoubleClick()\"\n (keydown)=\"onSplitterKeyDown($event)\"\n aria-label=\"Resize columns pane\"\n title=\"Drag to resize. Double-click to collapse/restore.\"\n ></button>\n }\n\n <!-- Scrollable timeline pane (horizontal scroll is limited to this area). -->\n <div\n class=\"mt-timeline-scroll min-w-0 flex-1 overflow-x-auto overflow-y-hidden\"\n #ganttScrollViewport\n >\n <div class=\"min-w-full\" [style.width.px]=\"ganttCanvasWidth()\">\n <mt-timeline-gantt-header\n [timelineMode]=\"timelineMode()\"\n [resolvedColumns]=\"resolvedColumns()\"\n [orderedGanttSegments]=\"orderedGanttSegments()\"\n [ganttCanvasWidth]=\"ganttCanvasWidth()\"\n [effectiveGanttSegmentWidthPx]=\"effectiveGanttSegmentWidthPx()\"\n [renderColumns]=\"false\"\n />\n\n <div>\n @for (item of resolvedGanttItems(); track $index) {\n <mt-timeline-gantt-row\n [item]=\"item\"\n [resolvedColumns]=\"[]\"\n [ganttCanvasWidth]=\"ganttCanvasWidth()\"\n [laneInsetPx]=\"laneInsetPx\"\n [columnTemplatesByKey]=\"columnTemplatesByKey()\"\n [progressTemplateDirective]=\"progressTemplateDirective()\"\n [renderColumns]=\"false\"\n (toggleCollapse)=\"onToggleCollapse($event)\"\n (progressClick)=\"onRowProgressClick($event)\"\n />\n }\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{display:block;min-width:0;max-width:100%}.mt-timeline-scroll{min-width:0;scrollbar-width:thin;scrollbar-color:var(--p-surface-400) transparent}.mt-timeline-scroll::-webkit-scrollbar{height:10px}.mt-timeline-scroll::-webkit-scrollbar-track{background:color-mix(in srgb,var(--p-surface-200) 30%,transparent);border-radius:9999px}.mt-timeline-scroll::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--p-surface-500) 60%,transparent);border-radius:9999px;border:2px solid transparent;background-clip:content-box}.mt-timeline-scroll::-webkit-scrollbar-thumb:hover{background:color-mix(in srgb,var(--p-surface-600) 65%,transparent);background-clip:content-box}.mt-timeline-splitter{position:relative;width:10px;min-width:10px;border:0;overflow:visible;background:linear-gradient(90deg,transparent 0,transparent calc(50% - 4px),color-mix(in srgb,var(--p-surface-300) 45%,transparent) calc(50% - 4px),color-mix(in srgb,var(--p-surface-300) 45%,transparent) calc(50% + 4px),transparent calc(50% + 4px),transparent 100%),linear-gradient(90deg,transparent 0,transparent calc(50% - 1px),color-mix(in srgb,var(--p-surface-500) 72%,white 28%) calc(50% - 1px),color-mix(in srgb,var(--p-surface-500) 72%,white 28%) calc(50% + 1px),transparent calc(50% + 1px),transparent 100%);cursor:col-resize;touch-action:none;transition:background .14s ease,opacity .14s ease,filter .14s ease}.mt-timeline-splitter:hover,.mt-timeline-splitter-active,.mt-timeline-splitter:focus-visible{background:linear-gradient(90deg,transparent 0,transparent calc(50% - 4px),color-mix(in srgb,var(--p-primary-color) 22%,transparent) calc(50% - 4px),color-mix(in srgb,var(--p-primary-color) 22%,transparent) calc(50% + 4px),transparent calc(50% + 4px),transparent 100%),linear-gradient(90deg,transparent 0,transparent calc(50% - 1px),color-mix(in srgb,var(--p-primary-color) 88%,white 12%) calc(50% - 1px),color-mix(in srgb,var(--p-primary-color) 88%,white 12%) calc(50% + 1px),transparent calc(50% + 1px),transparent 100%)}.mt-timeline-splitter:before{content:\"\";position:absolute;top:50%;inset-inline-start:50%;width:16px;height:42px;transform:translate(-50%,-50%) scale(1);opacity:1;border-radius:9999px;border:1px solid color-mix(in srgb,var(--p-surface-300) 72%,transparent);background:var(--p-surface-0);box-shadow:0 6px 16px -14px color-mix(in srgb,var(--p-surface-400) 18%,transparent),0 2px 6px -4px color-mix(in srgb,black 16%,transparent);pointer-events:none;transition:opacity .14s ease,transform .14s ease,border-color .14s ease,box-shadow .14s ease}.mt-timeline-splitter:after{content:\"\";position:absolute;top:50%;inset-inline-start:50%;width:6px;height:16px;transform:translate(-50%,-50%) scale(1);opacity:1;border-radius:9999px;background:linear-gradient(90deg,color-mix(in srgb,var(--p-surface-500) 78%,white 22%) 0,color-mix(in srgb,var(--p-surface-500) 78%,white 22%) 2px,transparent 2px,transparent 4px,color-mix(in srgb,var(--p-surface-500) 78%,white 22%) 4px,color-mix(in srgb,var(--p-surface-500) 78%,white 22%) 6px);pointer-events:none;transition:opacity .14s ease,transform .14s ease,background .14s ease}.mt-timeline-splitter:hover:before,.mt-timeline-splitter-active:before,.mt-timeline-splitter:focus-visible:before{border-color:color-mix(in srgb,var(--p-primary-color) 18%,var(--p-surface-200));box-shadow:0 8px 18px -16px color-mix(in srgb,var(--p-primary-color) 28%,transparent),0 4px 8px -6px color-mix(in srgb,black 18%,transparent)}.mt-timeline-splitter:hover:after,.mt-timeline-splitter-active:after,.mt-timeline-splitter:focus-visible:after{background:linear-gradient(90deg,color-mix(in srgb,var(--p-primary-color) 88%,white 12%) 0,color-mix(in srgb,var(--p-primary-color) 88%,white 12%) 2px,transparent 2px,transparent 4px,color-mix(in srgb,var(--p-primary-color) 88%,white 12%) 4px,color-mix(in srgb,var(--p-primary-color) 88%,white 12%) 6px)}.mt-timeline-splitter:hover{filter:saturate(1.03)}.mt-timeline-splitter:focus-visible{outline:none}.mt-timeline-splitter:focus-visible:before{box-shadow:0 0 0 3px color-mix(in srgb,var(--p-primary-color) 14%,transparent),0 8px 18px -16px color-mix(in srgb,var(--p-primary-color) 28%,transparent),0 4px 8px -6px color-mix(in srgb,black 18%,transparent)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: TimelineGanttHeader, selector: "mt-timeline-gantt-header", inputs: ["timelineMode", "resolvedColumns", "orderedGanttSegments", "ganttCanvasWidth", "effectiveGanttSegmentWidthPx", "renderColumns", "renderTimeline"] }, { kind: "component", type: TimelineGanttRow, selector: "mt-timeline-gantt-row", inputs: ["item", "resolvedColumns", "ganttCanvasWidth", "laneInsetPx", "renderColumns", "renderTimeline", "columnTemplatesByKey", "progressTemplateDirective"], outputs: ["toggleCollapse", "progressClick"] }] });
|
|
848
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: TimelineGantt, isStandalone: true, selector: "mt-timeline-gantt", inputs: { timelineMode: { classPropertyName: "timelineMode", publicName: "timelineMode", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, ganttTitleColumnLabel: { classPropertyName: "ganttTitleColumnLabel", publicName: "ganttTitleColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, ganttStatusColumnLabel: { classPropertyName: "ganttStatusColumnLabel", publicName: "ganttStatusColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, ganttInitiativeColumnWidthPx: { classPropertyName: "ganttInitiativeColumnWidthPx", publicName: "ganttInitiativeColumnWidthPx", isSignal: true, isRequired: false, transformFunction: null }, ganttStatusColumnWidthPx: { classPropertyName: "ganttStatusColumnWidthPx", publicName: "ganttStatusColumnWidthPx", isSignal: true, isRequired: false, transformFunction: null }, showGanttStatusColumn: { classPropertyName: "showGanttStatusColumn", publicName: "showGanttStatusColumn", isSignal: true, isRequired: false, transformFunction: null }, ganttSegmentWidthPx: { classPropertyName: "ganttSegmentWidthPx", publicName: "ganttSegmentWidthPx", isSignal: true, isRequired: false, transformFunction: null }, defaultVisibleColumns: { classPropertyName: "defaultVisibleColumns", publicName: "defaultVisibleColumns", isSignal: true, isRequired: false, transformFunction: null }, columnsPaneMinWidthPx: { classPropertyName: "columnsPaneMinWidthPx", publicName: "columnsPaneMinWidthPx", isSignal: true, isRequired: false, transformFunction: null }, columnsPaneMaxWidthPx: { classPropertyName: "columnsPaneMaxWidthPx", publicName: "columnsPaneMaxWidthPx", isSignal: true, isRequired: false, transformFunction: null }, enableVirtualScroll: { classPropertyName: "enableVirtualScroll", publicName: "enableVirtualScroll", isSignal: true, isRequired: false, transformFunction: null }, virtualScrollThreshold: { classPropertyName: "virtualScrollThreshold", publicName: "virtualScrollThreshold", isSignal: true, isRequired: false, transformFunction: null }, virtualScrollBodyHeightPx: { classPropertyName: "virtualScrollBodyHeightPx", publicName: "virtualScrollBodyHeightPx", isSignal: true, isRequired: false, transformFunction: null }, zoomToFit: { classPropertyName: "zoomToFit", publicName: "zoomToFit", isSignal: true, isRequired: false, transformFunction: null }, mappedGanttNodes: { classPropertyName: "mappedGanttNodes", publicName: "mappedGanttNodes", isSignal: true, isRequired: false, transformFunction: null }, orderedGanttSegments: { classPropertyName: "orderedGanttSegments", publicName: "orderedGanttSegments", isSignal: true, isRequired: false, transformFunction: null }, collapsedGanttIds: { classPropertyName: "collapsedGanttIds", publicName: "collapsedGanttIds", isSignal: true, isRequired: false, transformFunction: null }, columnTemplatesByKey: { classPropertyName: "columnTemplatesByKey", publicName: "columnTemplatesByKey", isSignal: true, isRequired: false, transformFunction: null }, progressTemplateDirective: { classPropertyName: "progressTemplateDirective", publicName: "progressTemplateDirective", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggleCollapse: "toggleCollapse", progressClick: "progressClick", timelineViewportWidthChange: "timelineViewportWidthChange" }, viewQueries: [{ propertyName: "ganttSplitContainerRef", first: true, predicate: ["ganttSplitContainer"], descendants: true }, { propertyName: "ganttScrollViewportRef", first: true, predicate: ["ganttScrollViewport"], descendants: true }, { propertyName: "columnsHeaderScrollViewport", first: true, predicate: ["columnsHeaderScrollViewport"], descendants: true }, { propertyName: "columnsBodyScrollViewportRef", first: true, predicate: ["columnsBodyScrollViewport"], descendants: true }, { propertyName: "timelineHeaderScrollViewport", first: true, predicate: ["timelineHeaderScrollViewport"], descendants: true }, { propertyName: "timelineBodyScrollViewport", first: true, predicate: ["timelineBodyScrollViewport"], descendants: true }], ngImport: i0, template: "<!-- Gantt view shell: scroll viewport + sized timeline table. -->\n<div\n class=\"min-h-56 min-w-0 overflow-hidden py-2\"\n *transloco=\"let t; prefix: 'timeline'\"\n>\n @if (isVirtualScrollActive()) {\n <div\n class=\"flex min-w-0 border border-surface bg-content\"\n #ganttSplitContainer\n >\n <div\n class=\"shrink-0 overflow-hidden\"\n [style.width.px]=\"resolvedColumnsPaneWidthPx()\"\n >\n <div\n #columnsHeaderScrollViewport\n class=\"mt-timeline-scroll mt-timeline-scrollbar-hidden overflow-x-auto overflow-y-hidden\"\n (scroll)=\"onColumnsHeaderScroll($event)\"\n >\n <div class=\"min-w-full\" [style.width.px]=\"columnsContentWidthPx()\">\n <mt-timeline-gantt-header\n [timelineMode]=\"timelineMode()\"\n [resolvedColumns]=\"resolvedColumns()\"\n [orderedGanttSegments]=\"orderedGanttSegments()\"\n [ganttCanvasWidth]=\"0\"\n [effectiveGanttSegmentWidthPx]=\"effectiveGanttSegmentWidthPx()\"\n [zoomToFit]=\"zoomToFit()\"\n [renderTimeline]=\"false\"\n />\n </div>\n </div>\n </div>\n\n @if (canResizeColumnsPane()) {\n <button\n type=\"button\"\n class=\"mt-timeline-splitter shrink-0\"\n [class.mt-timeline-splitter-active]=\"isColumnsResizing()\"\n (pointerdown)=\"onSplitterPointerDown($event)\"\n (dblclick)=\"onSplitterDoubleClick()\"\n (keydown)=\"onSplitterKeyDown($event)\"\n [attr.aria-label]=\"t('resize-columns')\"\n [title]=\"t('resize-columns-hint')\"\n ></button>\n }\n\n <div class=\"min-w-0 flex-1 overflow-hidden\">\n <div\n #timelineHeaderScrollViewport\n class=\"mt-timeline-scroll mt-timeline-scrollbar-hidden overflow-x-auto overflow-y-hidden\"\n (scroll)=\"onTimelineHeaderScroll($event)\"\n >\n <div class=\"min-w-full\" [style.width.px]=\"ganttCanvasWidth()\">\n <mt-timeline-gantt-header\n [timelineMode]=\"timelineMode()\"\n [resolvedColumns]=\"resolvedColumns()\"\n [orderedGanttSegments]=\"orderedGanttSegments()\"\n [ganttCanvasWidth]=\"ganttCanvasWidth()\"\n [effectiveGanttSegmentWidthPx]=\"effectiveGanttSegmentWidthPx()\"\n [zoomToFit]=\"zoomToFit()\"\n [renderColumns]=\"false\"\n />\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"flex min-w-0 border border-t-0 border-surface bg-content\">\n <div\n class=\"shrink-0 overflow-hidden\"\n [style.width.px]=\"resolvedColumnsPaneWidthPx()\"\n >\n <div\n #columnsBodyScrollViewport\n class=\"mt-timeline-scroll overflow-auto\"\n [style.height.px]=\"resolvedVirtualScrollBodyHeightPx()\"\n (scroll)=\"onColumnsBodyScroll($event)\"\n >\n <div class=\"min-w-full\" [style.width.px]=\"columnsContentWidthPx()\">\n @if (virtualScrollTopSpacerHeightPx() > 0) {\n <div [style.height.px]=\"virtualScrollTopSpacerHeightPx()\"></div>\n }\n\n @for (item of visibleResolvedGanttItems(); track item.id) {\n <mt-timeline-gantt-row\n [item]=\"item\"\n [resolvedColumns]=\"resolvedColumns()\"\n [ganttCanvasWidth]=\"0\"\n [laneInsetPx]=\"laneInsetPx()\"\n [columnTemplatesByKey]=\"columnTemplatesByKey()\"\n [progressTemplateDirective]=\"progressTemplateDirective()\"\n [renderTimeline]=\"false\"\n (toggleCollapse)=\"onToggleCollapse($event)\"\n (progressClick)=\"onRowProgressClick($event)\"\n />\n }\n\n @if (virtualScrollBottomSpacerHeightPx() > 0) {\n <div\n [style.height.px]=\"virtualScrollBottomSpacerHeightPx()\"\n ></div>\n }\n </div>\n </div>\n </div>\n\n @if (canResizeColumnsPane()) {\n <div\n class=\"w-[10px] min-w-[10px] shrink-0 border-e border-surface bg-surface-50\"\n aria-hidden=\"true\"\n ></div>\n }\n\n <div class=\"min-w-0 flex-1 overflow-hidden\">\n <div\n #timelineBodyScrollViewport\n #ganttScrollViewport\n class=\"mt-timeline-scroll overflow-auto\"\n [style.height.px]=\"resolvedVirtualScrollBodyHeightPx()\"\n (scroll)=\"onTimelineBodyScroll($event)\"\n >\n <div class=\"min-w-full\" [style.width.px]=\"ganttCanvasWidth()\">\n @if (virtualScrollTopSpacerHeightPx() > 0) {\n <div [style.height.px]=\"virtualScrollTopSpacerHeightPx()\"></div>\n }\n\n @for (item of visibleResolvedGanttItems(); track item.id) {\n <mt-timeline-gantt-row\n [item]=\"item\"\n [resolvedColumns]=\"[]\"\n [ganttCanvasWidth]=\"ganttCanvasWidth()\"\n [laneInsetPx]=\"laneInsetPx()\"\n [columnTemplatesByKey]=\"columnTemplatesByKey()\"\n [progressTemplateDirective]=\"progressTemplateDirective()\"\n [renderColumns]=\"false\"\n (toggleCollapse)=\"onToggleCollapse($event)\"\n (progressClick)=\"onRowProgressClick($event)\"\n />\n }\n\n @if (virtualScrollBottomSpacerHeightPx() > 0) {\n <div\n [style.height.px]=\"virtualScrollBottomSpacerHeightPx()\"\n ></div>\n }\n </div>\n </div>\n </div>\n </div>\n } @else {\n <div\n class=\"flex min-w-0 border border-surface bg-content\"\n #ganttSplitContainer\n >\n <!-- Fixed metadata columns pane. -->\n <div\n class=\"shrink-0 overflow-hidden\"\n [style.width.px]=\"resolvedColumnsPaneWidthPx()\"\n >\n <div\n class=\"mt-timeline-scroll h-full overflow-x-auto overflow-y-hidden\"\n >\n <div class=\"min-w-full\" [style.width.px]=\"columnsContentWidthPx()\">\n <mt-timeline-gantt-header\n [timelineMode]=\"timelineMode()\"\n [resolvedColumns]=\"resolvedColumns()\"\n [orderedGanttSegments]=\"orderedGanttSegments()\"\n [ganttCanvasWidth]=\"0\"\n [effectiveGanttSegmentWidthPx]=\"effectiveGanttSegmentWidthPx()\"\n [zoomToFit]=\"zoomToFit()\"\n [renderTimeline]=\"false\"\n />\n\n <div>\n @for (item of resolvedGanttItems(); track item.id) {\n <mt-timeline-gantt-row\n [item]=\"item\"\n [resolvedColumns]=\"resolvedColumns()\"\n [ganttCanvasWidth]=\"0\"\n [laneInsetPx]=\"laneInsetPx()\"\n [columnTemplatesByKey]=\"columnTemplatesByKey()\"\n [progressTemplateDirective]=\"progressTemplateDirective()\"\n [renderTimeline]=\"false\"\n (toggleCollapse)=\"onToggleCollapse($event)\"\n (progressClick)=\"onRowProgressClick($event)\"\n />\n }\n </div>\n </div>\n </div>\n </div>\n\n @if (canResizeColumnsPane()) {\n <button\n type=\"button\"\n class=\"mt-timeline-splitter shrink-0\"\n [class.mt-timeline-splitter-active]=\"isColumnsResizing()\"\n (pointerdown)=\"onSplitterPointerDown($event)\"\n (dblclick)=\"onSplitterDoubleClick()\"\n (keydown)=\"onSplitterKeyDown($event)\"\n [attr.aria-label]=\"t('resize-columns')\"\n [title]=\"t('resize-columns-hint')\"\n ></button>\n }\n\n <!-- Scrollable timeline pane (horizontal scroll is limited to this area). -->\n <div\n class=\"mt-timeline-scroll min-w-0 flex-1 overflow-x-auto overflow-y-hidden\"\n #ganttScrollViewport\n >\n <div class=\"min-w-full\" [style.width.px]=\"ganttCanvasWidth()\">\n <mt-timeline-gantt-header\n [timelineMode]=\"timelineMode()\"\n [resolvedColumns]=\"resolvedColumns()\"\n [orderedGanttSegments]=\"orderedGanttSegments()\"\n [ganttCanvasWidth]=\"ganttCanvasWidth()\"\n [effectiveGanttSegmentWidthPx]=\"effectiveGanttSegmentWidthPx()\"\n [zoomToFit]=\"zoomToFit()\"\n [renderColumns]=\"false\"\n />\n\n <div>\n @for (item of resolvedGanttItems(); track item.id) {\n <mt-timeline-gantt-row\n [item]=\"item\"\n [resolvedColumns]=\"[]\"\n [ganttCanvasWidth]=\"ganttCanvasWidth()\"\n [laneInsetPx]=\"laneInsetPx()\"\n [columnTemplatesByKey]=\"columnTemplatesByKey()\"\n [progressTemplateDirective]=\"progressTemplateDirective()\"\n [renderColumns]=\"false\"\n (toggleCollapse)=\"onToggleCollapse($event)\"\n (progressClick)=\"onRowProgressClick($event)\"\n />\n }\n </div>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: [":host{display:block;min-width:0;max-width:100%}.mt-timeline-scroll{min-width:0;scrollbar-width:thin;scrollbar-color:var(--p-surface-400) transparent}.mt-timeline-scroll::-webkit-scrollbar{height:10px}.mt-timeline-scroll::-webkit-scrollbar-track{background:color-mix(in srgb,var(--p-surface-200) 30%,transparent);border-radius:9999px}.mt-timeline-scroll::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--p-surface-500) 60%,transparent);border-radius:9999px;border:2px solid transparent;background-clip:content-box}.mt-timeline-scroll::-webkit-scrollbar-thumb:hover{background:color-mix(in srgb,var(--p-surface-600) 65%,transparent);background-clip:content-box}.mt-timeline-scrollbar-hidden{scrollbar-width:none}.mt-timeline-scrollbar-hidden::-webkit-scrollbar{width:0;height:0}.mt-timeline-splitter{position:relative;width:10px;min-width:10px;border:0;overflow:visible;background:linear-gradient(90deg,transparent 0,transparent calc(50% - 4px),color-mix(in srgb,var(--p-surface-300) 45%,transparent) calc(50% - 4px),color-mix(in srgb,var(--p-surface-300) 45%,transparent) calc(50% + 4px),transparent calc(50% + 4px),transparent 100%),linear-gradient(90deg,transparent 0,transparent calc(50% - 1px),color-mix(in srgb,var(--p-surface-500) 72%,white 28%) calc(50% - 1px),color-mix(in srgb,var(--p-surface-500) 72%,white 28%) calc(50% + 1px),transparent calc(50% + 1px),transparent 100%);cursor:col-resize;touch-action:none;transition:background .14s ease,opacity .14s ease,filter .14s ease}.mt-timeline-splitter:hover,.mt-timeline-splitter-active,.mt-timeline-splitter:focus-visible{background:linear-gradient(90deg,transparent 0,transparent calc(50% - 4px),color-mix(in srgb,var(--p-primary-color) 22%,transparent) calc(50% - 4px),color-mix(in srgb,var(--p-primary-color) 22%,transparent) calc(50% + 4px),transparent calc(50% + 4px),transparent 100%),linear-gradient(90deg,transparent 0,transparent calc(50% - 1px),color-mix(in srgb,var(--p-primary-color) 88%,white 12%) calc(50% - 1px),color-mix(in srgb,var(--p-primary-color) 88%,white 12%) calc(50% + 1px),transparent calc(50% + 1px),transparent 100%)}.mt-timeline-splitter:before{content:\"\";position:absolute;top:50%;inset-inline-start:50%;width:16px;height:42px;transform:translate(-50%,-50%) scale(1);opacity:1;border-radius:9999px;border:1px solid color-mix(in srgb,var(--p-surface-300) 72%,transparent);background:var(--p-surface-0);box-shadow:0 6px 16px -14px color-mix(in srgb,var(--p-surface-400) 18%,transparent),0 2px 6px -4px color-mix(in srgb,black 16%,transparent);pointer-events:none;transition:opacity .14s ease,transform .14s ease,border-color .14s ease,box-shadow .14s ease}.mt-timeline-splitter:after{content:\"\";position:absolute;top:50%;inset-inline-start:50%;width:6px;height:16px;transform:translate(-50%,-50%) scale(1);opacity:1;border-radius:9999px;background:linear-gradient(90deg,color-mix(in srgb,var(--p-surface-500) 78%,white 22%) 0,color-mix(in srgb,var(--p-surface-500) 78%,white 22%) 2px,transparent 2px,transparent 4px,color-mix(in srgb,var(--p-surface-500) 78%,white 22%) 4px,color-mix(in srgb,var(--p-surface-500) 78%,white 22%) 6px);pointer-events:none;transition:opacity .14s ease,transform .14s ease,background .14s ease}.mt-timeline-splitter:hover:before,.mt-timeline-splitter-active:before,.mt-timeline-splitter:focus-visible:before{border-color:color-mix(in srgb,var(--p-primary-color) 18%,var(--p-surface-200));box-shadow:0 8px 18px -16px color-mix(in srgb,var(--p-primary-color) 28%,transparent),0 4px 8px -6px color-mix(in srgb,black 18%,transparent)}.mt-timeline-splitter:hover:after,.mt-timeline-splitter-active:after,.mt-timeline-splitter:focus-visible:after{background:linear-gradient(90deg,color-mix(in srgb,var(--p-primary-color) 88%,white 12%) 0,color-mix(in srgb,var(--p-primary-color) 88%,white 12%) 2px,transparent 2px,transparent 4px,color-mix(in srgb,var(--p-primary-color) 88%,white 12%) 4px,color-mix(in srgb,var(--p-primary-color) 88%,white 12%) 6px)}.mt-timeline-splitter:hover{filter:saturate(1.03)}.mt-timeline-splitter:focus-visible{outline:none}.mt-timeline-splitter:focus-visible:before{box-shadow:0 0 0 3px color-mix(in srgb,var(--p-primary-color) 14%,transparent),0 8px 18px -16px color-mix(in srgb,var(--p-primary-color) 28%,transparent),0 4px 8px -6px color-mix(in srgb,black 18%,transparent)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: TimelineGanttHeader, selector: "mt-timeline-gantt-header", inputs: ["timelineMode", "resolvedColumns", "orderedGanttSegments", "ganttCanvasWidth", "effectiveGanttSegmentWidthPx", "zoomToFit", "renderColumns", "renderTimeline"] }, { kind: "component", type: TimelineGanttRow, selector: "mt-timeline-gantt-row", inputs: ["item", "resolvedColumns", "ganttCanvasWidth", "laneInsetPx", "renderColumns", "renderTimeline", "columnTemplatesByKey", "progressTemplateDirective"], outputs: ["toggleCollapse", "progressClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
724
849
|
}
|
|
725
850
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: TimelineGantt, decorators: [{
|
|
726
851
|
type: Component,
|
|
727
|
-
args: [{ selector: 'mt-timeline-gantt', standalone: true,
|
|
852
|
+
args: [{ selector: 'mt-timeline-gantt', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
853
|
+
CommonModule,
|
|
854
|
+
TranslocoDirective,
|
|
855
|
+
TimelineGanttHeader,
|
|
856
|
+
TimelineGanttRow,
|
|
857
|
+
], template: "<!-- Gantt view shell: scroll viewport + sized timeline table. -->\n<div\n class=\"min-h-56 min-w-0 overflow-hidden py-2\"\n *transloco=\"let t; prefix: 'timeline'\"\n>\n @if (isVirtualScrollActive()) {\n <div\n class=\"flex min-w-0 border border-surface bg-content\"\n #ganttSplitContainer\n >\n <div\n class=\"shrink-0 overflow-hidden\"\n [style.width.px]=\"resolvedColumnsPaneWidthPx()\"\n >\n <div\n #columnsHeaderScrollViewport\n class=\"mt-timeline-scroll mt-timeline-scrollbar-hidden overflow-x-auto overflow-y-hidden\"\n (scroll)=\"onColumnsHeaderScroll($event)\"\n >\n <div class=\"min-w-full\" [style.width.px]=\"columnsContentWidthPx()\">\n <mt-timeline-gantt-header\n [timelineMode]=\"timelineMode()\"\n [resolvedColumns]=\"resolvedColumns()\"\n [orderedGanttSegments]=\"orderedGanttSegments()\"\n [ganttCanvasWidth]=\"0\"\n [effectiveGanttSegmentWidthPx]=\"effectiveGanttSegmentWidthPx()\"\n [zoomToFit]=\"zoomToFit()\"\n [renderTimeline]=\"false\"\n />\n </div>\n </div>\n </div>\n\n @if (canResizeColumnsPane()) {\n <button\n type=\"button\"\n class=\"mt-timeline-splitter shrink-0\"\n [class.mt-timeline-splitter-active]=\"isColumnsResizing()\"\n (pointerdown)=\"onSplitterPointerDown($event)\"\n (dblclick)=\"onSplitterDoubleClick()\"\n (keydown)=\"onSplitterKeyDown($event)\"\n [attr.aria-label]=\"t('resize-columns')\"\n [title]=\"t('resize-columns-hint')\"\n ></button>\n }\n\n <div class=\"min-w-0 flex-1 overflow-hidden\">\n <div\n #timelineHeaderScrollViewport\n class=\"mt-timeline-scroll mt-timeline-scrollbar-hidden overflow-x-auto overflow-y-hidden\"\n (scroll)=\"onTimelineHeaderScroll($event)\"\n >\n <div class=\"min-w-full\" [style.width.px]=\"ganttCanvasWidth()\">\n <mt-timeline-gantt-header\n [timelineMode]=\"timelineMode()\"\n [resolvedColumns]=\"resolvedColumns()\"\n [orderedGanttSegments]=\"orderedGanttSegments()\"\n [ganttCanvasWidth]=\"ganttCanvasWidth()\"\n [effectiveGanttSegmentWidthPx]=\"effectiveGanttSegmentWidthPx()\"\n [zoomToFit]=\"zoomToFit()\"\n [renderColumns]=\"false\"\n />\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"flex min-w-0 border border-t-0 border-surface bg-content\">\n <div\n class=\"shrink-0 overflow-hidden\"\n [style.width.px]=\"resolvedColumnsPaneWidthPx()\"\n >\n <div\n #columnsBodyScrollViewport\n class=\"mt-timeline-scroll overflow-auto\"\n [style.height.px]=\"resolvedVirtualScrollBodyHeightPx()\"\n (scroll)=\"onColumnsBodyScroll($event)\"\n >\n <div class=\"min-w-full\" [style.width.px]=\"columnsContentWidthPx()\">\n @if (virtualScrollTopSpacerHeightPx() > 0) {\n <div [style.height.px]=\"virtualScrollTopSpacerHeightPx()\"></div>\n }\n\n @for (item of visibleResolvedGanttItems(); track item.id) {\n <mt-timeline-gantt-row\n [item]=\"item\"\n [resolvedColumns]=\"resolvedColumns()\"\n [ganttCanvasWidth]=\"0\"\n [laneInsetPx]=\"laneInsetPx()\"\n [columnTemplatesByKey]=\"columnTemplatesByKey()\"\n [progressTemplateDirective]=\"progressTemplateDirective()\"\n [renderTimeline]=\"false\"\n (toggleCollapse)=\"onToggleCollapse($event)\"\n (progressClick)=\"onRowProgressClick($event)\"\n />\n }\n\n @if (virtualScrollBottomSpacerHeightPx() > 0) {\n <div\n [style.height.px]=\"virtualScrollBottomSpacerHeightPx()\"\n ></div>\n }\n </div>\n </div>\n </div>\n\n @if (canResizeColumnsPane()) {\n <div\n class=\"w-[10px] min-w-[10px] shrink-0 border-e border-surface bg-surface-50\"\n aria-hidden=\"true\"\n ></div>\n }\n\n <div class=\"min-w-0 flex-1 overflow-hidden\">\n <div\n #timelineBodyScrollViewport\n #ganttScrollViewport\n class=\"mt-timeline-scroll overflow-auto\"\n [style.height.px]=\"resolvedVirtualScrollBodyHeightPx()\"\n (scroll)=\"onTimelineBodyScroll($event)\"\n >\n <div class=\"min-w-full\" [style.width.px]=\"ganttCanvasWidth()\">\n @if (virtualScrollTopSpacerHeightPx() > 0) {\n <div [style.height.px]=\"virtualScrollTopSpacerHeightPx()\"></div>\n }\n\n @for (item of visibleResolvedGanttItems(); track item.id) {\n <mt-timeline-gantt-row\n [item]=\"item\"\n [resolvedColumns]=\"[]\"\n [ganttCanvasWidth]=\"ganttCanvasWidth()\"\n [laneInsetPx]=\"laneInsetPx()\"\n [columnTemplatesByKey]=\"columnTemplatesByKey()\"\n [progressTemplateDirective]=\"progressTemplateDirective()\"\n [renderColumns]=\"false\"\n (toggleCollapse)=\"onToggleCollapse($event)\"\n (progressClick)=\"onRowProgressClick($event)\"\n />\n }\n\n @if (virtualScrollBottomSpacerHeightPx() > 0) {\n <div\n [style.height.px]=\"virtualScrollBottomSpacerHeightPx()\"\n ></div>\n }\n </div>\n </div>\n </div>\n </div>\n } @else {\n <div\n class=\"flex min-w-0 border border-surface bg-content\"\n #ganttSplitContainer\n >\n <!-- Fixed metadata columns pane. -->\n <div\n class=\"shrink-0 overflow-hidden\"\n [style.width.px]=\"resolvedColumnsPaneWidthPx()\"\n >\n <div\n class=\"mt-timeline-scroll h-full overflow-x-auto overflow-y-hidden\"\n >\n <div class=\"min-w-full\" [style.width.px]=\"columnsContentWidthPx()\">\n <mt-timeline-gantt-header\n [timelineMode]=\"timelineMode()\"\n [resolvedColumns]=\"resolvedColumns()\"\n [orderedGanttSegments]=\"orderedGanttSegments()\"\n [ganttCanvasWidth]=\"0\"\n [effectiveGanttSegmentWidthPx]=\"effectiveGanttSegmentWidthPx()\"\n [zoomToFit]=\"zoomToFit()\"\n [renderTimeline]=\"false\"\n />\n\n <div>\n @for (item of resolvedGanttItems(); track item.id) {\n <mt-timeline-gantt-row\n [item]=\"item\"\n [resolvedColumns]=\"resolvedColumns()\"\n [ganttCanvasWidth]=\"0\"\n [laneInsetPx]=\"laneInsetPx()\"\n [columnTemplatesByKey]=\"columnTemplatesByKey()\"\n [progressTemplateDirective]=\"progressTemplateDirective()\"\n [renderTimeline]=\"false\"\n (toggleCollapse)=\"onToggleCollapse($event)\"\n (progressClick)=\"onRowProgressClick($event)\"\n />\n }\n </div>\n </div>\n </div>\n </div>\n\n @if (canResizeColumnsPane()) {\n <button\n type=\"button\"\n class=\"mt-timeline-splitter shrink-0\"\n [class.mt-timeline-splitter-active]=\"isColumnsResizing()\"\n (pointerdown)=\"onSplitterPointerDown($event)\"\n (dblclick)=\"onSplitterDoubleClick()\"\n (keydown)=\"onSplitterKeyDown($event)\"\n [attr.aria-label]=\"t('resize-columns')\"\n [title]=\"t('resize-columns-hint')\"\n ></button>\n }\n\n <!-- Scrollable timeline pane (horizontal scroll is limited to this area). -->\n <div\n class=\"mt-timeline-scroll min-w-0 flex-1 overflow-x-auto overflow-y-hidden\"\n #ganttScrollViewport\n >\n <div class=\"min-w-full\" [style.width.px]=\"ganttCanvasWidth()\">\n <mt-timeline-gantt-header\n [timelineMode]=\"timelineMode()\"\n [resolvedColumns]=\"resolvedColumns()\"\n [orderedGanttSegments]=\"orderedGanttSegments()\"\n [ganttCanvasWidth]=\"ganttCanvasWidth()\"\n [effectiveGanttSegmentWidthPx]=\"effectiveGanttSegmentWidthPx()\"\n [zoomToFit]=\"zoomToFit()\"\n [renderColumns]=\"false\"\n />\n\n <div>\n @for (item of resolvedGanttItems(); track item.id) {\n <mt-timeline-gantt-row\n [item]=\"item\"\n [resolvedColumns]=\"[]\"\n [ganttCanvasWidth]=\"ganttCanvasWidth()\"\n [laneInsetPx]=\"laneInsetPx()\"\n [columnTemplatesByKey]=\"columnTemplatesByKey()\"\n [progressTemplateDirective]=\"progressTemplateDirective()\"\n [renderColumns]=\"false\"\n (toggleCollapse)=\"onToggleCollapse($event)\"\n (progressClick)=\"onRowProgressClick($event)\"\n />\n }\n </div>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: [":host{display:block;min-width:0;max-width:100%}.mt-timeline-scroll{min-width:0;scrollbar-width:thin;scrollbar-color:var(--p-surface-400) transparent}.mt-timeline-scroll::-webkit-scrollbar{height:10px}.mt-timeline-scroll::-webkit-scrollbar-track{background:color-mix(in srgb,var(--p-surface-200) 30%,transparent);border-radius:9999px}.mt-timeline-scroll::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--p-surface-500) 60%,transparent);border-radius:9999px;border:2px solid transparent;background-clip:content-box}.mt-timeline-scroll::-webkit-scrollbar-thumb:hover{background:color-mix(in srgb,var(--p-surface-600) 65%,transparent);background-clip:content-box}.mt-timeline-scrollbar-hidden{scrollbar-width:none}.mt-timeline-scrollbar-hidden::-webkit-scrollbar{width:0;height:0}.mt-timeline-splitter{position:relative;width:10px;min-width:10px;border:0;overflow:visible;background:linear-gradient(90deg,transparent 0,transparent calc(50% - 4px),color-mix(in srgb,var(--p-surface-300) 45%,transparent) calc(50% - 4px),color-mix(in srgb,var(--p-surface-300) 45%,transparent) calc(50% + 4px),transparent calc(50% + 4px),transparent 100%),linear-gradient(90deg,transparent 0,transparent calc(50% - 1px),color-mix(in srgb,var(--p-surface-500) 72%,white 28%) calc(50% - 1px),color-mix(in srgb,var(--p-surface-500) 72%,white 28%) calc(50% + 1px),transparent calc(50% + 1px),transparent 100%);cursor:col-resize;touch-action:none;transition:background .14s ease,opacity .14s ease,filter .14s ease}.mt-timeline-splitter:hover,.mt-timeline-splitter-active,.mt-timeline-splitter:focus-visible{background:linear-gradient(90deg,transparent 0,transparent calc(50% - 4px),color-mix(in srgb,var(--p-primary-color) 22%,transparent) calc(50% - 4px),color-mix(in srgb,var(--p-primary-color) 22%,transparent) calc(50% + 4px),transparent calc(50% + 4px),transparent 100%),linear-gradient(90deg,transparent 0,transparent calc(50% - 1px),color-mix(in srgb,var(--p-primary-color) 88%,white 12%) calc(50% - 1px),color-mix(in srgb,var(--p-primary-color) 88%,white 12%) calc(50% + 1px),transparent calc(50% + 1px),transparent 100%)}.mt-timeline-splitter:before{content:\"\";position:absolute;top:50%;inset-inline-start:50%;width:16px;height:42px;transform:translate(-50%,-50%) scale(1);opacity:1;border-radius:9999px;border:1px solid color-mix(in srgb,var(--p-surface-300) 72%,transparent);background:var(--p-surface-0);box-shadow:0 6px 16px -14px color-mix(in srgb,var(--p-surface-400) 18%,transparent),0 2px 6px -4px color-mix(in srgb,black 16%,transparent);pointer-events:none;transition:opacity .14s ease,transform .14s ease,border-color .14s ease,box-shadow .14s ease}.mt-timeline-splitter:after{content:\"\";position:absolute;top:50%;inset-inline-start:50%;width:6px;height:16px;transform:translate(-50%,-50%) scale(1);opacity:1;border-radius:9999px;background:linear-gradient(90deg,color-mix(in srgb,var(--p-surface-500) 78%,white 22%) 0,color-mix(in srgb,var(--p-surface-500) 78%,white 22%) 2px,transparent 2px,transparent 4px,color-mix(in srgb,var(--p-surface-500) 78%,white 22%) 4px,color-mix(in srgb,var(--p-surface-500) 78%,white 22%) 6px);pointer-events:none;transition:opacity .14s ease,transform .14s ease,background .14s ease}.mt-timeline-splitter:hover:before,.mt-timeline-splitter-active:before,.mt-timeline-splitter:focus-visible:before{border-color:color-mix(in srgb,var(--p-primary-color) 18%,var(--p-surface-200));box-shadow:0 8px 18px -16px color-mix(in srgb,var(--p-primary-color) 28%,transparent),0 4px 8px -6px color-mix(in srgb,black 18%,transparent)}.mt-timeline-splitter:hover:after,.mt-timeline-splitter-active:after,.mt-timeline-splitter:focus-visible:after{background:linear-gradient(90deg,color-mix(in srgb,var(--p-primary-color) 88%,white 12%) 0,color-mix(in srgb,var(--p-primary-color) 88%,white 12%) 2px,transparent 2px,transparent 4px,color-mix(in srgb,var(--p-primary-color) 88%,white 12%) 4px,color-mix(in srgb,var(--p-primary-color) 88%,white 12%) 6px)}.mt-timeline-splitter:hover{filter:saturate(1.03)}.mt-timeline-splitter:focus-visible{outline:none}.mt-timeline-splitter:focus-visible:before{box-shadow:0 0 0 3px color-mix(in srgb,var(--p-primary-color) 14%,transparent),0 8px 18px -16px color-mix(in srgb,var(--p-primary-color) 28%,transparent),0 4px 8px -6px color-mix(in srgb,black 18%,transparent)}\n"] }]
|
|
728
858
|
}], propDecorators: { ganttSplitContainerRef: [{
|
|
729
859
|
type: ViewChild,
|
|
730
860
|
args: ['ganttSplitContainer']
|
|
731
861
|
}], ganttScrollViewportRef: [{
|
|
732
862
|
type: ViewChild,
|
|
733
863
|
args: ['ganttScrollViewport']
|
|
734
|
-
}],
|
|
864
|
+
}], columnsHeaderScrollViewport: [{
|
|
865
|
+
type: ViewChild,
|
|
866
|
+
args: ['columnsHeaderScrollViewport']
|
|
867
|
+
}], columnsBodyScrollViewportRef: [{
|
|
868
|
+
type: ViewChild,
|
|
869
|
+
args: ['columnsBodyScrollViewport']
|
|
870
|
+
}], timelineHeaderScrollViewport: [{
|
|
871
|
+
type: ViewChild,
|
|
872
|
+
args: ['timelineHeaderScrollViewport']
|
|
873
|
+
}], timelineBodyScrollViewport: [{
|
|
874
|
+
type: ViewChild,
|
|
875
|
+
args: ['timelineBodyScrollViewport']
|
|
876
|
+
}], timelineMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "timelineMode", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], ganttTitleColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttTitleColumnLabel", required: false }] }], ganttStatusColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttStatusColumnLabel", required: false }] }], ganttInitiativeColumnWidthPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttInitiativeColumnWidthPx", required: false }] }], ganttStatusColumnWidthPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttStatusColumnWidthPx", required: false }] }], showGanttStatusColumn: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGanttStatusColumn", required: false }] }], ganttSegmentWidthPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttSegmentWidthPx", required: false }] }], defaultVisibleColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultVisibleColumns", required: false }] }], columnsPaneMinWidthPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnsPaneMinWidthPx", required: false }] }], columnsPaneMaxWidthPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnsPaneMaxWidthPx", required: false }] }], enableVirtualScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableVirtualScroll", required: false }] }], virtualScrollThreshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualScrollThreshold", required: false }] }], virtualScrollBodyHeightPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualScrollBodyHeightPx", required: false }] }], zoomToFit: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoomToFit", required: false }] }], mappedGanttNodes: [{ type: i0.Input, args: [{ isSignal: true, alias: "mappedGanttNodes", required: false }] }], orderedGanttSegments: [{ type: i0.Input, args: [{ isSignal: true, alias: "orderedGanttSegments", required: false }] }], collapsedGanttIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsedGanttIds", required: false }] }], columnTemplatesByKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnTemplatesByKey", required: false }] }], progressTemplateDirective: [{ type: i0.Input, args: [{ isSignal: true, alias: "progressTemplateDirective", required: false }] }], toggleCollapse: [{ type: i0.Output, args: ["toggleCollapse"] }], progressClick: [{ type: i0.Output, args: ["progressClick"] }], timelineViewportWidthChange: [{ type: i0.Output, args: ["timelineViewportWidthChange"] }] } });
|
|
735
877
|
|
|
736
878
|
/**
|
|
737
879
|
* Shared timeline header.
|
|
@@ -745,7 +887,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
|
|
|
745
887
|
* the parent timeline container.
|
|
746
888
|
*/
|
|
747
889
|
class TimelineHeader {
|
|
748
|
-
title = input('
|
|
890
|
+
title = input('', ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
749
891
|
timelineMode = input('quarterly', ...(ngDevMode ? [{ debugName: "timelineMode" }] : []));
|
|
750
892
|
timelineModeOptions = input([], ...(ngDevMode ? [{ debugName: "timelineModeOptions" }] : []));
|
|
751
893
|
timelineModeChange = output();
|
|
@@ -754,20 +896,27 @@ class TimelineHeader {
|
|
|
754
896
|
this.timelineModeChange.emit(mode);
|
|
755
897
|
}
|
|
756
898
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: TimelineHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
757
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.3", type: TimelineHeader, isStandalone: true, selector: "mt-timeline-header", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, timelineMode: { classPropertyName: "timelineMode", publicName: "timelineMode", isSignal: true, isRequired: false, transformFunction: null }, timelineModeOptions: { classPropertyName: "timelineModeOptions", publicName: "timelineModeOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { timelineModeChange: "timelineModeChange" }, ngImport: i0, template: "<!-- Timeline title + scale mode selector. -->\
|
|
899
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.3", type: TimelineHeader, isStandalone: true, selector: "mt-timeline-header", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, timelineMode: { classPropertyName: "timelineMode", publicName: "timelineMode", isSignal: true, isRequired: false, transformFunction: null }, timelineModeOptions: { classPropertyName: "timelineModeOptions", publicName: "timelineModeOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { timelineModeChange: "timelineModeChange" }, ngImport: i0, template: "<!-- Timeline title + scale mode selector. -->\n<div\n class=\"flex flex-wrap items-center justify-between gap-3\"\n *transloco=\"let t; prefix: 'timeline'\"\n>\n <div class=\"flex items-center gap-2\">\n <mt-icon icon=\"custom.timeline-point\" class=\"text-base\"></mt-icon>\n <h3 class=\"text-base font-semibold\">{{ title() || t(\"timeline\") }}</h3>\n </div>\n\n <!-- Scale mode switcher (monthly/quarterly/etc.). -->\n <div class=\"flex flex-wrap items-center justify-end gap-2\">\n <div class=\"w-44\">\n <mt-select-field\n [field]=\"false\"\n [options]=\"timelineModeOptions()\"\n optionLabel=\"label\"\n optionValue=\"value\"\n optionIcon=\"icon\"\n optionIconColor=\"color\"\n optionAvatarShape=\"circle\"\n [showClear]=\"false\"\n [hasPlaceholderPrefix]=\"false\"\n [placeholder]=\"t('view')\"\n [ngModel]=\"timelineMode()\"\n (onChange)=\"onTimelineModeChange($event)\"\n />\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape"], outputs: ["onChange"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
758
900
|
}
|
|
759
901
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: TimelineHeader, decorators: [{
|
|
760
902
|
type: Component,
|
|
761
|
-
args: [{ selector: 'mt-timeline-header', standalone: true, imports: [FormsModule, SelectField, Icon], template: "<!-- Timeline title + scale mode selector. -->\
|
|
903
|
+
args: [{ selector: 'mt-timeline-header', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [FormsModule, TranslocoDirective, SelectField, Icon], template: "<!-- Timeline title + scale mode selector. -->\n<div\n class=\"flex flex-wrap items-center justify-between gap-3\"\n *transloco=\"let t; prefix: 'timeline'\"\n>\n <div class=\"flex items-center gap-2\">\n <mt-icon icon=\"custom.timeline-point\" class=\"text-base\"></mt-icon>\n <h3 class=\"text-base font-semibold\">{{ title() || t(\"timeline\") }}</h3>\n </div>\n\n <!-- Scale mode switcher (monthly/quarterly/etc.). -->\n <div class=\"flex flex-wrap items-center justify-end gap-2\">\n <div class=\"w-44\">\n <mt-select-field\n [field]=\"false\"\n [options]=\"timelineModeOptions()\"\n optionLabel=\"label\"\n optionValue=\"value\"\n optionIcon=\"icon\"\n optionIconColor=\"color\"\n optionAvatarShape=\"circle\"\n [showClear]=\"false\"\n [hasPlaceholderPrefix]=\"false\"\n [placeholder]=\"t('view')\"\n [ngModel]=\"timelineMode()\"\n (onChange)=\"onTimelineModeChange($event)\"\n />\n </div>\n </div>\n</div>\n" }]
|
|
762
904
|
}], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], timelineMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "timelineMode", required: false }] }], timelineModeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "timelineModeOptions", required: false }] }], timelineModeChange: [{ type: i0.Output, args: ["timelineModeChange"] }] } });
|
|
763
905
|
|
|
764
|
-
// Default scale
|
|
765
|
-
const
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
906
|
+
// Default scale mode values (labels are resolved at runtime via i18n).
|
|
907
|
+
const DEFAULT_TIMELINE_MODE_VALUES = [
|
|
908
|
+
'monthly',
|
|
909
|
+
'quarterly',
|
|
910
|
+
'biannually',
|
|
911
|
+
'annually',
|
|
770
912
|
];
|
|
913
|
+
const ZOOM_TO_FIT_VIEW = 'zoom-to-fit';
|
|
914
|
+
const ZOOM_TO_FIT_MIN_CELL_WIDTH_PX = {
|
|
915
|
+
monthly: 56,
|
|
916
|
+
quarterly: 72,
|
|
917
|
+
biannually: 84,
|
|
918
|
+
annually: 96,
|
|
919
|
+
};
|
|
771
920
|
/**
|
|
772
921
|
* Dynamic Gantt timeline component.
|
|
773
922
|
*
|
|
@@ -787,14 +936,44 @@ class Timeline {
|
|
|
787
936
|
detailsPopover;
|
|
788
937
|
// Invisible anchor used to position popover at exact mouse click coordinates.
|
|
789
938
|
detailsPopoverClickAnchor;
|
|
790
|
-
|
|
791
|
-
|
|
939
|
+
transloco = inject(TranslocoService);
|
|
940
|
+
destroyRef = inject(DestroyRef);
|
|
941
|
+
activeLang = signal(this.transloco.getActiveLang(), ...(ngDevMode ? [{ debugName: "activeLang" }] : []));
|
|
942
|
+
timelineViewportWidthPx = signal(0, ...(ngDevMode ? [{ debugName: "timelineViewportWidthPx" }] : []));
|
|
943
|
+
title = input('', ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
944
|
+
emptyMessage = input('', ...(ngDevMode ? [{ debugName: "emptyMessage" }] : []));
|
|
792
945
|
isLoading = input(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
793
946
|
showHeader = input(true, ...(ngDevMode ? [{ debugName: "showHeader" }] : []));
|
|
947
|
+
showZoomToFitControl = input(false, ...(ngDevMode ? [{ debugName: "showZoomToFitControl" }] : []));
|
|
794
948
|
timelineMode = model('quarterly', ...(ngDevMode ? [{ debugName: "timelineMode" }] : []));
|
|
795
|
-
timelineModeOptions = input([
|
|
796
|
-
|
|
797
|
-
|
|
949
|
+
timelineModeOptions = input([], ...(ngDevMode ? [{ debugName: "timelineModeOptions" }] : []));
|
|
950
|
+
resolvedTimelineModeOptions = computed(() => {
|
|
951
|
+
const options = this.timelineModeOptions();
|
|
952
|
+
this.activeLang(); // reactive dependency for language changes
|
|
953
|
+
const baseOptions = options.length
|
|
954
|
+
? options
|
|
955
|
+
: DEFAULT_TIMELINE_MODE_VALUES.map((value) => ({
|
|
956
|
+
label: this.transloco.translate(`timeline.mode-${value}`),
|
|
957
|
+
value,
|
|
958
|
+
}));
|
|
959
|
+
if (!this.showZoomToFitControl()) {
|
|
960
|
+
return baseOptions;
|
|
961
|
+
}
|
|
962
|
+
const hasZoomToFitOption = baseOptions.some((option) => option.value === ZOOM_TO_FIT_VIEW);
|
|
963
|
+
if (hasZoomToFitOption) {
|
|
964
|
+
return baseOptions;
|
|
965
|
+
}
|
|
966
|
+
return [
|
|
967
|
+
...baseOptions,
|
|
968
|
+
{
|
|
969
|
+
label: this.transloco.translate('timeline.mode-zoom-to-fit'),
|
|
970
|
+
value: ZOOM_TO_FIT_VIEW,
|
|
971
|
+
icon: 'layout.maximize-01',
|
|
972
|
+
color: 'primary',
|
|
973
|
+
},
|
|
974
|
+
];
|
|
975
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedTimelineModeOptions" }] : []));
|
|
976
|
+
zoomToFit = model(false, ...(ngDevMode ? [{ debugName: "zoomToFit" }] : []));
|
|
798
977
|
ganttData = input([], ...(ngDevMode ? [{ debugName: "ganttData" }] : []));
|
|
799
978
|
ganttMapping = input(null, ...(ngDevMode ? [{ debugName: "ganttMapping" }] : []));
|
|
800
979
|
// Null means "legacy defaults". Empty array means "progress-only" mode.
|
|
@@ -803,9 +982,12 @@ class Timeline {
|
|
|
803
982
|
defaultVisibleColumns = input(4, ...(ngDevMode ? [{ debugName: "defaultVisibleColumns" }] : []));
|
|
804
983
|
columnsPaneMinWidthPx = input(0, ...(ngDevMode ? [{ debugName: "columnsPaneMinWidthPx" }] : []));
|
|
805
984
|
columnsPaneMaxWidthPx = input(null, ...(ngDevMode ? [{ debugName: "columnsPaneMaxWidthPx" }] : []));
|
|
985
|
+
enableVirtualScroll = input(true, ...(ngDevMode ? [{ debugName: "enableVirtualScroll" }] : []));
|
|
986
|
+
virtualScrollThreshold = input(120, ...(ngDevMode ? [{ debugName: "virtualScrollThreshold" }] : []));
|
|
987
|
+
virtualScrollBodyHeightPx = input(640, ...(ngDevMode ? [{ debugName: "virtualScrollBodyHeightPx" }] : []));
|
|
806
988
|
// Backward-compatible defaults when `columns` is null.
|
|
807
|
-
ganttTitleColumnLabel = input('
|
|
808
|
-
ganttStatusColumnLabel = input('
|
|
989
|
+
ganttTitleColumnLabel = input('', ...(ngDevMode ? [{ debugName: "ganttTitleColumnLabel" }] : []));
|
|
990
|
+
ganttStatusColumnLabel = input('', ...(ngDevMode ? [{ debugName: "ganttStatusColumnLabel" }] : []));
|
|
809
991
|
ganttInitiativeColumnWidthPx = input(288, ...(ngDevMode ? [{ debugName: "ganttInitiativeColumnWidthPx" }] : []));
|
|
810
992
|
ganttStatusColumnWidthPx = input(160, ...(ngDevMode ? [{ debugName: "ganttStatusColumnWidthPx" }] : []));
|
|
811
993
|
showGanttStatusColumn = input(false, ...(ngDevMode ? [{ debugName: "showGanttStatusColumn" }] : []));
|
|
@@ -844,26 +1026,54 @@ class Timeline {
|
|
|
844
1026
|
.map((item) => this.mapNode(item, mapping, state))
|
|
845
1027
|
.filter((item) => item !== null);
|
|
846
1028
|
}, ...(ngDevMode ? [{ debugName: "mappedGanttNodes" }] : []));
|
|
1029
|
+
selectedTimelineView = computed(() => {
|
|
1030
|
+
if (this.timelineMode() === ZOOM_TO_FIT_VIEW || this.zoomToFit()) {
|
|
1031
|
+
return ZOOM_TO_FIT_VIEW;
|
|
1032
|
+
}
|
|
1033
|
+
return this.timelineMode();
|
|
1034
|
+
}, ...(ngDevMode ? [{ debugName: "selectedTimelineView" }] : []));
|
|
1035
|
+
resolvedTimelineRange = computed(() => this.resolveDateRange(this.mappedGanttNodes()), ...(ngDevMode ? [{ debugName: "resolvedTimelineRange" }] : []));
|
|
1036
|
+
resolvedRenderTimelineMode = computed(() => {
|
|
1037
|
+
const selectedView = this.selectedTimelineView();
|
|
1038
|
+
if (selectedView !== ZOOM_TO_FIT_VIEW) {
|
|
1039
|
+
return selectedView;
|
|
1040
|
+
}
|
|
1041
|
+
const range = this.resolvedTimelineRange();
|
|
1042
|
+
if (!range) {
|
|
1043
|
+
return 'quarterly';
|
|
1044
|
+
}
|
|
1045
|
+
return this.resolveZoomToFitMode(range.startAt, range.endAt, this.timelineViewportWidthPx());
|
|
1046
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedRenderTimelineMode" }] : []));
|
|
847
1047
|
// Builds time columns from the mapped data date range and selected mode.
|
|
848
1048
|
orderedGanttSegments = computed(() => {
|
|
849
|
-
const
|
|
850
|
-
if (!nodes.length) {
|
|
851
|
-
return [];
|
|
852
|
-
}
|
|
853
|
-
const range = this.resolveDateRange(nodes);
|
|
1049
|
+
const range = this.resolvedTimelineRange();
|
|
854
1050
|
if (!range) {
|
|
855
1051
|
return [];
|
|
856
1052
|
}
|
|
857
|
-
return this.buildSegments(range.startAt, range.endAt, this.
|
|
1053
|
+
return this.buildSegments(range.startAt, range.endAt, this.resolvedRenderTimelineMode());
|
|
858
1054
|
}, ...(ngDevMode ? [{ debugName: "orderedGanttSegments" }] : []));
|
|
1055
|
+
isZoomToFitActive = computed(() => this.selectedTimelineView() === ZOOM_TO_FIT_VIEW, ...(ngDevMode ? [{ debugName: "isZoomToFitActive" }] : []));
|
|
1056
|
+
constructor() {
|
|
1057
|
+
this.transloco.langChanges$
|
|
1058
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
1059
|
+
.subscribe((lang) => this.activeLang.set(lang));
|
|
1060
|
+
}
|
|
859
1061
|
// Timeline mode is controlled by parent model signal to keep it externally bindable.
|
|
860
1062
|
onTimelineModeChange(mode) {
|
|
861
|
-
if (!mode || this.
|
|
1063
|
+
if (!mode || this.selectedTimelineView() === mode) {
|
|
862
1064
|
return;
|
|
863
1065
|
}
|
|
1066
|
+
const enableZoomToFit = mode === ZOOM_TO_FIT_VIEW;
|
|
1067
|
+
this.zoomToFit.set(enableZoomToFit);
|
|
864
1068
|
this.timelineMode.set(mode);
|
|
865
1069
|
this.timelineModeChangeEvent.emit(mode);
|
|
866
1070
|
}
|
|
1071
|
+
onTimelineViewportWidthChange(widthPx) {
|
|
1072
|
+
if (!Number.isFinite(widthPx) || widthPx <= 0) {
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
this.timelineViewportWidthPx.set(widthPx);
|
|
1076
|
+
}
|
|
867
1077
|
// Handles row progress clicks: emits public event and opens details popover.
|
|
868
1078
|
onGanttProgressClick(item, event) {
|
|
869
1079
|
event.stopPropagation();
|
|
@@ -920,7 +1130,8 @@ class Timeline {
|
|
|
920
1130
|
}
|
|
921
1131
|
// Recursively maps consumer item shape using accessors from `ganttMapping`.
|
|
922
1132
|
mapNode(source, mapping, state) {
|
|
923
|
-
const title = this.resolveString(source, mapping.title) ??
|
|
1133
|
+
const title = this.resolveString(source, mapping.title) ??
|
|
1134
|
+
this.transloco.translate('timeline.untitled');
|
|
924
1135
|
const idRaw = this.resolveAccessor(source, mapping.id);
|
|
925
1136
|
const id = idRaw !== null && idRaw !== undefined && String(idRaw).length
|
|
926
1137
|
? idRaw
|
|
@@ -984,10 +1195,24 @@ class Timeline {
|
|
|
984
1195
|
return this.genSegments(startAt, endAt, 6, (month) => `H ${month < 6 ? 1 : 2}`, 'H');
|
|
985
1196
|
case 'annually':
|
|
986
1197
|
return this.genSegments(startAt, endAt, 12, () => 'Y', 'Y');
|
|
987
|
-
default:
|
|
988
|
-
return this.genSegments(startAt, endAt, 3, (month) => `Q ${Math.floor(month / 3) + 1}`, 'Q');
|
|
989
1198
|
}
|
|
990
1199
|
}
|
|
1200
|
+
resolveZoomToFitMode(startAt, endAt, viewportWidthPx) {
|
|
1201
|
+
const availableWidthPx = viewportWidthPx > 0
|
|
1202
|
+
? viewportWidthPx
|
|
1203
|
+
: Math.max(this.ganttSegmentWidthPx() * 4, 320);
|
|
1204
|
+
for (const mode of DEFAULT_TIMELINE_MODE_VALUES) {
|
|
1205
|
+
const segments = this.buildSegments(startAt, endAt, mode);
|
|
1206
|
+
if (!segments.length) {
|
|
1207
|
+
continue;
|
|
1208
|
+
}
|
|
1209
|
+
const fittedCellWidthPx = availableWidthPx / segments.length;
|
|
1210
|
+
if (fittedCellWidthPx >= ZOOM_TO_FIT_MIN_CELL_WIDTH_PX[mode]) {
|
|
1211
|
+
return mode;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
return 'annually';
|
|
1215
|
+
}
|
|
991
1216
|
// Generates contiguous segments between range boundaries.
|
|
992
1217
|
genSegments(startAt, endAt, stepMonths, title, prefix) {
|
|
993
1218
|
const segments = [];
|
|
@@ -1136,25 +1361,26 @@ class Timeline {
|
|
|
1136
1361
|
return anchor;
|
|
1137
1362
|
}
|
|
1138
1363
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: Timeline, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1139
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: Timeline, isStandalone: true, selector: "mt-timeline", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null }, showHeader: { classPropertyName: "showHeader", publicName: "showHeader", isSignal: true, isRequired: false, transformFunction: null }, timelineMode: { classPropertyName: "timelineMode", publicName: "timelineMode", isSignal: true, isRequired: false, transformFunction: null }, timelineModeOptions: { classPropertyName: "timelineModeOptions", publicName: "timelineModeOptions", isSignal: true, isRequired: false, transformFunction: null }, ganttData: { classPropertyName: "ganttData", publicName: "ganttData", isSignal: true, isRequired: false, transformFunction: null }, ganttMapping: { classPropertyName: "ganttMapping", publicName: "ganttMapping", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, ganttSegmentWidthPx: { classPropertyName: "ganttSegmentWidthPx", publicName: "ganttSegmentWidthPx", isSignal: true, isRequired: false, transformFunction: null }, defaultVisibleColumns: { classPropertyName: "defaultVisibleColumns", publicName: "defaultVisibleColumns", isSignal: true, isRequired: false, transformFunction: null }, columnsPaneMinWidthPx: { classPropertyName: "columnsPaneMinWidthPx", publicName: "columnsPaneMinWidthPx", isSignal: true, isRequired: false, transformFunction: null }, columnsPaneMaxWidthPx: { classPropertyName: "columnsPaneMaxWidthPx", publicName: "columnsPaneMaxWidthPx", isSignal: true, isRequired: false, transformFunction: null }, ganttTitleColumnLabel: { classPropertyName: "ganttTitleColumnLabel", publicName: "ganttTitleColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, ganttStatusColumnLabel: { classPropertyName: "ganttStatusColumnLabel", publicName: "ganttStatusColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, ganttInitiativeColumnWidthPx: { classPropertyName: "ganttInitiativeColumnWidthPx", publicName: "ganttInitiativeColumnWidthPx", isSignal: true, isRequired: false, transformFunction: null }, ganttStatusColumnWidthPx: { classPropertyName: "ganttStatusColumnWidthPx", publicName: "ganttStatusColumnWidthPx", isSignal: true, isRequired: false, transformFunction: null }, showGanttStatusColumn: { classPropertyName: "showGanttStatusColumn", publicName: "showGanttStatusColumn", isSignal: true, isRequired: false, transformFunction: null }, showGanttDetailsPopup: { classPropertyName: "showGanttDetailsPopup", publicName: "showGanttDetailsPopup", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { timelineMode: "timelineModeChange", timelineModeChangeEvent: "timelineModeChangeEvent", ganttItemClick: "ganttItemClick" }, host: { classAttribute: "block" }, queries: [{ propertyName: "ganttTemplate", first: true, predicate: TimelineGanttTemplateDirective, descendants: true, isSignal: true }, { propertyName: "columnTemplates", predicate: (TimelineColumnTemplateDirective), isSignal: true }, { propertyName: "popoverTemplate", first: true, predicate: (TimelinePopoverTemplateDirective), descendants: true, isSignal: true }, { propertyName: "progressTemplate", first: true, predicate: (TimelineProgressTemplateDirective), descendants: true, isSignal: true }], viewQueries: [{ propertyName: "detailsPopover", first: true, predicate: ["detailsPopover"], descendants: true }, { propertyName: "detailsPopoverClickAnchor", first: true, predicate: ["detailsPopoverClickAnchor"], descendants: true }], ngImport: i0, template: "<div class=\"mt-3 block overflow-hidden\">\n <!-- Optional top header (title + timeline mode selector). -->\n @if (showHeader()) {\n <mt-timeline-header\n [title]=\"title()\"\n [timelineMode]=\"timelineMode()\"\n [timelineModeOptions]=\"timelineModeOptions()\"\n (timelineModeChange)=\"onTimelineModeChange($event)\"\n />\n }\n\n <!-- Loading state. -->\n @if (isLoading()) {\n <div class=\"flex min-h-56 items-center justify-center p-8\">\n <div\n class=\"h-8 w-8 animate-spin rounded-full border-2 border-surface-200\"\n style=\"border-top-color: var(--p-primary-color)\"\n ></div>\n </div>\n } @else if (ganttTemplate(); as template) {\n <!-- Full custom gantt-body override. -->\n <div class=\"min-h-56 py-4\">\n <ng-container [ngTemplateOutlet]=\"template.templateRef\"></ng-container>\n </div>\n } @else if (\n mappedGanttNodes().length > 0 && orderedGanttSegments().length > 0\n ) {\n <!-- Built-in gantt renderer. -->\n <mt-timeline-gantt\n [timelineMode]=\"timelineMode()\"\n [columns]=\"columns()\"\n [ganttTitleColumnLabel]=\"ganttTitleColumnLabel()\"\n [ganttStatusColumnLabel]=\"ganttStatusColumnLabel()\"\n [ganttInitiativeColumnWidthPx]=\"ganttInitiativeColumnWidthPx()\"\n [ganttStatusColumnWidthPx]=\"ganttStatusColumnWidthPx()\"\n [showGanttStatusColumn]=\"showGanttStatusColumn()\"\n [ganttSegmentWidthPx]=\"ganttSegmentWidthPx()\"\n [defaultVisibleColumns]=\"defaultVisibleColumns()\"\n [columnsPaneMinWidthPx]=\"columnsPaneMinWidthPx()\"\n [columnsPaneMaxWidthPx]=\"columnsPaneMaxWidthPx()\"\n [mappedGanttNodes]=\"mappedGanttNodes()\"\n [orderedGanttSegments]=\"orderedGanttSegments()\"\n [collapsedGanttIds]=\"collapsedGanttIds()\"\n [columnTemplatesByKey]=\"columnTemplatesByKey()\"\n [progressTemplateDirective]=\"progressTemplate()\"\n (toggleCollapse)=\"toggleGanttCollapse($event)\"\n (progressClick)=\"onGanttProgressClick($event.item, $event.event)\"\n />\n } @else {\n <!-- Empty state. -->\n <div\n class=\"flex min-h-56 items-center justify-center p-8 text-sm text-surface-500\"\n >\n {{ emptyMessage() }}\n </div>\n }\n\n <!-- Details popover host (custom template or built-in fallback). -->\n <p-popover #detailsPopover appendTo=\"body\" (onHide)=\"onDetailsPopoverHide()\">\n @if (showGanttDetailsPopup() && selectedGanttItem(); as item) {\n @if (popoverTemplate(); as template) {\n <ng-container\n [ngTemplateOutlet]=\"template.templateRef\"\n [ngTemplateOutletContext]=\"getPopoverTemplateContext(item)\"\n ></ng-container>\n } @else {\n <mt-timeline-default-popover\n [item]=\"item\"\n (requestClose)=\"hideDetailsPopover()\"\n />\n }\n }\n </p-popover>\n\n <!-- Point anchor used to place the popover exactly where user clicked. -->\n <span\n #detailsPopoverClickAnchor\n class=\"pointer-events-none fixed h-0 w-0\"\n [style.left.px]=\"-9999\"\n [style.top.px]=\"-9999\"\n aria-hidden=\"true\"\n ></span>\n</div>\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: TimelineHeader, selector: "mt-timeline-header", inputs: ["title", "timelineMode", "timelineModeOptions"], outputs: ["timelineModeChange"] }, { kind: "component", type: TimelineGantt, selector: "mt-timeline-gantt", inputs: ["timelineMode", "columns", "ganttTitleColumnLabel", "ganttStatusColumnLabel", "ganttInitiativeColumnWidthPx", "ganttStatusColumnWidthPx", "showGanttStatusColumn", "ganttSegmentWidthPx", "defaultVisibleColumns", "columnsPaneMinWidthPx", "columnsPaneMaxWidthPx", "mappedGanttNodes", "orderedGanttSegments", "collapsedGanttIds", "columnTemplatesByKey", "progressTemplateDirective"], outputs: ["toggleCollapse", "progressClick"] }, { kind: "component", type: TimelineDefaultPopover, selector: "mt-timeline-default-popover", inputs: ["item"], outputs: ["requestClose"] }, { kind: "component", type: Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }] });
|
|
1364
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: Timeline, isStandalone: true, selector: "mt-timeline", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null }, showHeader: { classPropertyName: "showHeader", publicName: "showHeader", isSignal: true, isRequired: false, transformFunction: null }, showZoomToFitControl: { classPropertyName: "showZoomToFitControl", publicName: "showZoomToFitControl", isSignal: true, isRequired: false, transformFunction: null }, timelineMode: { classPropertyName: "timelineMode", publicName: "timelineMode", isSignal: true, isRequired: false, transformFunction: null }, timelineModeOptions: { classPropertyName: "timelineModeOptions", publicName: "timelineModeOptions", isSignal: true, isRequired: false, transformFunction: null }, zoomToFit: { classPropertyName: "zoomToFit", publicName: "zoomToFit", isSignal: true, isRequired: false, transformFunction: null }, ganttData: { classPropertyName: "ganttData", publicName: "ganttData", isSignal: true, isRequired: false, transformFunction: null }, ganttMapping: { classPropertyName: "ganttMapping", publicName: "ganttMapping", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, ganttSegmentWidthPx: { classPropertyName: "ganttSegmentWidthPx", publicName: "ganttSegmentWidthPx", isSignal: true, isRequired: false, transformFunction: null }, defaultVisibleColumns: { classPropertyName: "defaultVisibleColumns", publicName: "defaultVisibleColumns", isSignal: true, isRequired: false, transformFunction: null }, columnsPaneMinWidthPx: { classPropertyName: "columnsPaneMinWidthPx", publicName: "columnsPaneMinWidthPx", isSignal: true, isRequired: false, transformFunction: null }, columnsPaneMaxWidthPx: { classPropertyName: "columnsPaneMaxWidthPx", publicName: "columnsPaneMaxWidthPx", isSignal: true, isRequired: false, transformFunction: null }, enableVirtualScroll: { classPropertyName: "enableVirtualScroll", publicName: "enableVirtualScroll", isSignal: true, isRequired: false, transformFunction: null }, virtualScrollThreshold: { classPropertyName: "virtualScrollThreshold", publicName: "virtualScrollThreshold", isSignal: true, isRequired: false, transformFunction: null }, virtualScrollBodyHeightPx: { classPropertyName: "virtualScrollBodyHeightPx", publicName: "virtualScrollBodyHeightPx", isSignal: true, isRequired: false, transformFunction: null }, ganttTitleColumnLabel: { classPropertyName: "ganttTitleColumnLabel", publicName: "ganttTitleColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, ganttStatusColumnLabel: { classPropertyName: "ganttStatusColumnLabel", publicName: "ganttStatusColumnLabel", isSignal: true, isRequired: false, transformFunction: null }, ganttInitiativeColumnWidthPx: { classPropertyName: "ganttInitiativeColumnWidthPx", publicName: "ganttInitiativeColumnWidthPx", isSignal: true, isRequired: false, transformFunction: null }, ganttStatusColumnWidthPx: { classPropertyName: "ganttStatusColumnWidthPx", publicName: "ganttStatusColumnWidthPx", isSignal: true, isRequired: false, transformFunction: null }, showGanttStatusColumn: { classPropertyName: "showGanttStatusColumn", publicName: "showGanttStatusColumn", isSignal: true, isRequired: false, transformFunction: null }, showGanttDetailsPopup: { classPropertyName: "showGanttDetailsPopup", publicName: "showGanttDetailsPopup", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { timelineMode: "timelineModeChange", zoomToFit: "zoomToFitChange", timelineModeChangeEvent: "timelineModeChangeEvent", ganttItemClick: "ganttItemClick" }, host: { classAttribute: "block" }, queries: [{ propertyName: "ganttTemplate", first: true, predicate: TimelineGanttTemplateDirective, descendants: true, isSignal: true }, { propertyName: "columnTemplates", predicate: (TimelineColumnTemplateDirective), isSignal: true }, { propertyName: "popoverTemplate", first: true, predicate: (TimelinePopoverTemplateDirective), descendants: true, isSignal: true }, { propertyName: "progressTemplate", first: true, predicate: (TimelineProgressTemplateDirective), descendants: true, isSignal: true }], viewQueries: [{ propertyName: "detailsPopover", first: true, predicate: ["detailsPopover"], descendants: true }, { propertyName: "detailsPopoverClickAnchor", first: true, predicate: ["detailsPopoverClickAnchor"], descendants: true }], ngImport: i0, template: "<div class=\"mt-3 block overflow-hidden\" *transloco=\"let t; prefix: 'timeline'\">\n <!-- Optional top header (title + timeline mode selector). -->\n @if (showHeader()) {\n <mt-timeline-header\n [title]=\"title() || t('timeline')\"\n [timelineMode]=\"selectedTimelineView()\"\n [timelineModeOptions]=\"resolvedTimelineModeOptions()\"\n (timelineModeChange)=\"onTimelineModeChange($event)\"\n />\n }\n\n <!-- Loading state. -->\n @if (isLoading()) {\n <div class=\"flex min-h-56 items-center justify-center p-8\">\n <div\n class=\"h-8 w-8 animate-spin rounded-full border-2 border-surface-200\"\n style=\"border-top-color: var(--p-primary-color)\"\n ></div>\n </div>\n } @else if (ganttTemplate(); as template) {\n <!-- Full custom gantt-body override. -->\n <div class=\"min-h-56 py-4\">\n <ng-container [ngTemplateOutlet]=\"template.templateRef\"></ng-container>\n </div>\n } @else if (\n mappedGanttNodes().length > 0 && orderedGanttSegments().length > 0\n ) {\n <!-- Built-in gantt renderer. -->\n <mt-timeline-gantt\n [timelineMode]=\"resolvedRenderTimelineMode()\"\n [columns]=\"columns()\"\n [ganttTitleColumnLabel]=\"ganttTitleColumnLabel() || t('portfolio-name')\"\n [ganttStatusColumnLabel]=\"ganttStatusColumnLabel() || t('status')\"\n [ganttInitiativeColumnWidthPx]=\"ganttInitiativeColumnWidthPx()\"\n [ganttStatusColumnWidthPx]=\"ganttStatusColumnWidthPx()\"\n [showGanttStatusColumn]=\"showGanttStatusColumn()\"\n [ganttSegmentWidthPx]=\"ganttSegmentWidthPx()\"\n [defaultVisibleColumns]=\"defaultVisibleColumns()\"\n [columnsPaneMinWidthPx]=\"columnsPaneMinWidthPx()\"\n [columnsPaneMaxWidthPx]=\"columnsPaneMaxWidthPx()\"\n [enableVirtualScroll]=\"enableVirtualScroll()\"\n [virtualScrollThreshold]=\"virtualScrollThreshold()\"\n [virtualScrollBodyHeightPx]=\"virtualScrollBodyHeightPx()\"\n [zoomToFit]=\"isZoomToFitActive()\"\n [mappedGanttNodes]=\"mappedGanttNodes()\"\n [orderedGanttSegments]=\"orderedGanttSegments()\"\n [collapsedGanttIds]=\"collapsedGanttIds()\"\n [columnTemplatesByKey]=\"columnTemplatesByKey()\"\n [progressTemplateDirective]=\"progressTemplate()\"\n (timelineViewportWidthChange)=\"onTimelineViewportWidthChange($event)\"\n (toggleCollapse)=\"toggleGanttCollapse($event)\"\n (progressClick)=\"onGanttProgressClick($event.item, $event.event)\"\n />\n } @else {\n <!-- Empty state. -->\n <div\n class=\"flex min-h-56 items-center justify-center p-8 text-sm text-surface-500\"\n >\n {{ emptyMessage() || t(\"empty-message\") }}\n </div>\n }\n\n <!-- Details popover host (custom template or built-in fallback). -->\n <p-popover #detailsPopover appendTo=\"body\" (onHide)=\"onDetailsPopoverHide()\">\n @if (showGanttDetailsPopup() && selectedGanttItem(); as item) {\n @if (popoverTemplate(); as template) {\n <ng-container\n [ngTemplateOutlet]=\"template.templateRef\"\n [ngTemplateOutletContext]=\"getPopoverTemplateContext(item)\"\n ></ng-container>\n } @else {\n <mt-timeline-default-popover\n [item]=\"item\"\n (requestClose)=\"hideDetailsPopover()\"\n />\n }\n }\n </p-popover>\n\n <!-- Point anchor used to place the popover exactly where user clicked. -->\n <span\n #detailsPopoverClickAnchor\n class=\"pointer-events-none fixed h-0 w-0\"\n [style.left.px]=\"-9999\"\n [style.top.px]=\"-9999\"\n aria-hidden=\"true\"\n ></span>\n</div>\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: TimelineHeader, selector: "mt-timeline-header", inputs: ["title", "timelineMode", "timelineModeOptions"], outputs: ["timelineModeChange"] }, { kind: "component", type: TimelineGantt, selector: "mt-timeline-gantt", inputs: ["timelineMode", "columns", "ganttTitleColumnLabel", "ganttStatusColumnLabel", "ganttInitiativeColumnWidthPx", "ganttStatusColumnWidthPx", "showGanttStatusColumn", "ganttSegmentWidthPx", "defaultVisibleColumns", "columnsPaneMinWidthPx", "columnsPaneMaxWidthPx", "enableVirtualScroll", "virtualScrollThreshold", "virtualScrollBodyHeightPx", "zoomToFit", "mappedGanttNodes", "orderedGanttSegments", "collapsedGanttIds", "columnTemplatesByKey", "progressTemplateDirective"], outputs: ["toggleCollapse", "progressClick", "timelineViewportWidthChange"] }, { kind: "component", type: TimelineDefaultPopover, selector: "mt-timeline-default-popover", inputs: ["item"], outputs: ["requestClose"] }, { kind: "component", type: Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1140
1365
|
}
|
|
1141
1366
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: Timeline, decorators: [{
|
|
1142
1367
|
type: Component,
|
|
1143
|
-
args: [{ selector: 'mt-timeline', standalone: true, imports: [
|
|
1368
|
+
args: [{ selector: 'mt-timeline', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
1144
1369
|
CommonModule,
|
|
1370
|
+
TranslocoDirective,
|
|
1145
1371
|
TimelineHeader,
|
|
1146
1372
|
TimelineGantt,
|
|
1147
1373
|
TimelineDefaultPopover,
|
|
1148
1374
|
Popover,
|
|
1149
1375
|
NgTemplateOutlet,
|
|
1150
|
-
], host: { class: 'block' }, template: "<div class=\"mt-3 block overflow-hidden\">\n <!-- Optional top header (title + timeline mode selector). -->\n @if (showHeader()) {\n <mt-timeline-header\n [title]=\"title()\"\n [timelineMode]=\"
|
|
1151
|
-
}], propDecorators: { detailsPopover: [{
|
|
1376
|
+
], host: { class: 'block' }, template: "<div class=\"mt-3 block overflow-hidden\" *transloco=\"let t; prefix: 'timeline'\">\n <!-- Optional top header (title + timeline mode selector). -->\n @if (showHeader()) {\n <mt-timeline-header\n [title]=\"title() || t('timeline')\"\n [timelineMode]=\"selectedTimelineView()\"\n [timelineModeOptions]=\"resolvedTimelineModeOptions()\"\n (timelineModeChange)=\"onTimelineModeChange($event)\"\n />\n }\n\n <!-- Loading state. -->\n @if (isLoading()) {\n <div class=\"flex min-h-56 items-center justify-center p-8\">\n <div\n class=\"h-8 w-8 animate-spin rounded-full border-2 border-surface-200\"\n style=\"border-top-color: var(--p-primary-color)\"\n ></div>\n </div>\n } @else if (ganttTemplate(); as template) {\n <!-- Full custom gantt-body override. -->\n <div class=\"min-h-56 py-4\">\n <ng-container [ngTemplateOutlet]=\"template.templateRef\"></ng-container>\n </div>\n } @else if (\n mappedGanttNodes().length > 0 && orderedGanttSegments().length > 0\n ) {\n <!-- Built-in gantt renderer. -->\n <mt-timeline-gantt\n [timelineMode]=\"resolvedRenderTimelineMode()\"\n [columns]=\"columns()\"\n [ganttTitleColumnLabel]=\"ganttTitleColumnLabel() || t('portfolio-name')\"\n [ganttStatusColumnLabel]=\"ganttStatusColumnLabel() || t('status')\"\n [ganttInitiativeColumnWidthPx]=\"ganttInitiativeColumnWidthPx()\"\n [ganttStatusColumnWidthPx]=\"ganttStatusColumnWidthPx()\"\n [showGanttStatusColumn]=\"showGanttStatusColumn()\"\n [ganttSegmentWidthPx]=\"ganttSegmentWidthPx()\"\n [defaultVisibleColumns]=\"defaultVisibleColumns()\"\n [columnsPaneMinWidthPx]=\"columnsPaneMinWidthPx()\"\n [columnsPaneMaxWidthPx]=\"columnsPaneMaxWidthPx()\"\n [enableVirtualScroll]=\"enableVirtualScroll()\"\n [virtualScrollThreshold]=\"virtualScrollThreshold()\"\n [virtualScrollBodyHeightPx]=\"virtualScrollBodyHeightPx()\"\n [zoomToFit]=\"isZoomToFitActive()\"\n [mappedGanttNodes]=\"mappedGanttNodes()\"\n [orderedGanttSegments]=\"orderedGanttSegments()\"\n [collapsedGanttIds]=\"collapsedGanttIds()\"\n [columnTemplatesByKey]=\"columnTemplatesByKey()\"\n [progressTemplateDirective]=\"progressTemplate()\"\n (timelineViewportWidthChange)=\"onTimelineViewportWidthChange($event)\"\n (toggleCollapse)=\"toggleGanttCollapse($event)\"\n (progressClick)=\"onGanttProgressClick($event.item, $event.event)\"\n />\n } @else {\n <!-- Empty state. -->\n <div\n class=\"flex min-h-56 items-center justify-center p-8 text-sm text-surface-500\"\n >\n {{ emptyMessage() || t(\"empty-message\") }}\n </div>\n }\n\n <!-- Details popover host (custom template or built-in fallback). -->\n <p-popover #detailsPopover appendTo=\"body\" (onHide)=\"onDetailsPopoverHide()\">\n @if (showGanttDetailsPopup() && selectedGanttItem(); as item) {\n @if (popoverTemplate(); as template) {\n <ng-container\n [ngTemplateOutlet]=\"template.templateRef\"\n [ngTemplateOutletContext]=\"getPopoverTemplateContext(item)\"\n ></ng-container>\n } @else {\n <mt-timeline-default-popover\n [item]=\"item\"\n (requestClose)=\"hideDetailsPopover()\"\n />\n }\n }\n </p-popover>\n\n <!-- Point anchor used to place the popover exactly where user clicked. -->\n <span\n #detailsPopoverClickAnchor\n class=\"pointer-events-none fixed h-0 w-0\"\n [style.left.px]=\"-9999\"\n [style.top.px]=\"-9999\"\n aria-hidden=\"true\"\n ></span>\n</div>\n", styles: [":host{display:block}\n"] }]
|
|
1377
|
+
}], ctorParameters: () => [], propDecorators: { detailsPopover: [{
|
|
1152
1378
|
type: ViewChild,
|
|
1153
1379
|
args: ['detailsPopover']
|
|
1154
1380
|
}], detailsPopoverClickAnchor: [{
|
|
1155
1381
|
type: ViewChild,
|
|
1156
1382
|
args: ['detailsPopoverClickAnchor']
|
|
1157
|
-
}], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], isLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLoading", required: false }] }], showHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHeader", required: false }] }], timelineMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "timelineMode", required: false }] }, { type: i0.Output, args: ["timelineModeChange"] }], timelineModeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "timelineModeOptions", required: false }] }], ganttData: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttData", required: false }] }], ganttMapping: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttMapping", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], ganttSegmentWidthPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttSegmentWidthPx", required: false }] }], defaultVisibleColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultVisibleColumns", required: false }] }], columnsPaneMinWidthPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnsPaneMinWidthPx", required: false }] }], columnsPaneMaxWidthPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnsPaneMaxWidthPx", required: false }] }], ganttTitleColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttTitleColumnLabel", required: false }] }], ganttStatusColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttStatusColumnLabel", required: false }] }], ganttInitiativeColumnWidthPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttInitiativeColumnWidthPx", required: false }] }], ganttStatusColumnWidthPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttStatusColumnWidthPx", required: false }] }], showGanttStatusColumn: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGanttStatusColumn", required: false }] }], showGanttDetailsPopup: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGanttDetailsPopup", required: false }] }], timelineModeChangeEvent: [{ type: i0.Output, args: ["timelineModeChangeEvent"] }], ganttItemClick: [{ type: i0.Output, args: ["ganttItemClick"] }], ganttTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TimelineGanttTemplateDirective), { isSignal: true }] }], columnTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => TimelineColumnTemplateDirective), { isSignal: true }] }], popoverTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TimelinePopoverTemplateDirective), { isSignal: true }] }], progressTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TimelineProgressTemplateDirective), { isSignal: true }] }] } });
|
|
1383
|
+
}], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], isLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLoading", required: false }] }], showHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHeader", required: false }] }], showZoomToFitControl: [{ type: i0.Input, args: [{ isSignal: true, alias: "showZoomToFitControl", required: false }] }], timelineMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "timelineMode", required: false }] }, { type: i0.Output, args: ["timelineModeChange"] }], timelineModeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "timelineModeOptions", required: false }] }], zoomToFit: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoomToFit", required: false }] }, { type: i0.Output, args: ["zoomToFitChange"] }], ganttData: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttData", required: false }] }], ganttMapping: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttMapping", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], ganttSegmentWidthPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttSegmentWidthPx", required: false }] }], defaultVisibleColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultVisibleColumns", required: false }] }], columnsPaneMinWidthPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnsPaneMinWidthPx", required: false }] }], columnsPaneMaxWidthPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnsPaneMaxWidthPx", required: false }] }], enableVirtualScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableVirtualScroll", required: false }] }], virtualScrollThreshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualScrollThreshold", required: false }] }], virtualScrollBodyHeightPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualScrollBodyHeightPx", required: false }] }], ganttTitleColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttTitleColumnLabel", required: false }] }], ganttStatusColumnLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttStatusColumnLabel", required: false }] }], ganttInitiativeColumnWidthPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttInitiativeColumnWidthPx", required: false }] }], ganttStatusColumnWidthPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "ganttStatusColumnWidthPx", required: false }] }], showGanttStatusColumn: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGanttStatusColumn", required: false }] }], showGanttDetailsPopup: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGanttDetailsPopup", required: false }] }], timelineModeChangeEvent: [{ type: i0.Output, args: ["timelineModeChangeEvent"] }], ganttItemClick: [{ type: i0.Output, args: ["ganttItemClick"] }], ganttTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TimelineGanttTemplateDirective), { isSignal: true }] }], columnTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => TimelineColumnTemplateDirective), { isSignal: true }] }], popoverTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TimelinePopoverTemplateDirective), { isSignal: true }] }], progressTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TimelineProgressTemplateDirective), { isSignal: true }] }] } });
|
|
1158
1384
|
|
|
1159
1385
|
/**
|
|
1160
1386
|
* Generated bundle index. Do not edit.
|