@masterteam/timeline 0.0.9 → 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.
@@ -9,7 +9,6 @@ import { Tooltip } from 'primeng/tooltip';
9
9
  import { Icon } from '@masterteam/icons';
10
10
  import * as i1$1 from '@angular/forms';
11
11
  import { FormsModule } from '@angular/forms';
12
- import { Button } from '@masterteam/components/button';
13
12
  import { SelectField } from '@masterteam/components/select-field';
14
13
 
15
14
  class TimelineGanttTemplateDirective {
@@ -308,8 +307,16 @@ class TimelineGantt {
308
307
  this.ganttScrollViewport = value;
309
308
  this.bindResizeObserver();
310
309
  }
310
+ columnsHeaderScrollViewport;
311
+ set columnsBodyScrollViewportRef(value) {
312
+ this.columnsBodyScrollViewport = value;
313
+ this.ganttBodyScrollTopPx.set(value?.nativeElement.scrollTop ?? 0);
314
+ }
315
+ timelineHeaderScrollViewport;
316
+ timelineBodyScrollViewport;
311
317
  ganttSplitContainer;
312
318
  ganttScrollViewport;
319
+ columnsBodyScrollViewport;
313
320
  resizeObserver;
314
321
  splitResizeObserver;
315
322
  // Width of the visible horizontal viewport used for adaptive segment sizing.
@@ -318,12 +325,18 @@ class TimelineGantt {
318
325
  splitterWidthPx = 10;
319
326
  timelineLaneInsetPx = 12;
320
327
  adaptiveSegmentMaxWidthPx = 320;
328
+ ganttRowHeightPx = 64;
329
+ virtualScrollOverscanRows = 6;
321
330
  columnsPaneUserWidthPx = signal(null, ...(ngDevMode ? [{ debugName: "columnsPaneUserWidthPx" }] : []));
322
331
  lastExpandedColumnsPaneWidthPx = signal(null, ...(ngDevMode ? [{ debugName: "lastExpandedColumnsPaneWidthPx" }] : []));
332
+ ganttBodyScrollTopPx = signal(0, ...(ngDevMode ? [{ debugName: "ganttBodyScrollTopPx" }] : []));
323
333
  isColumnsResizing = signal(false, ...(ngDevMode ? [{ debugName: "isColumnsResizing" }] : []));
324
334
  canResizeColumnsPane = computed(() => this.baseResolvedColumns().length > 0, ...(ngDevMode ? [{ debugName: "canResizeColumnsPane" }] : []));
325
335
  resizeStartClientX = 0;
326
336
  resizeStartColumnsPaneWidthPx = 0;
337
+ isSyncingColumnsScroll = false;
338
+ isSyncingTimelineScroll = false;
339
+ isSyncingVerticalScroll = false;
327
340
  timelineMode = input('quarterly', ...(ngDevMode ? [{ debugName: "timelineMode" }] : []));
328
341
  columns = input(null, ...(ngDevMode ? [{ debugName: "columns" }] : []));
329
342
  ganttTitleColumnLabel = input('Portfolio Name', ...(ngDevMode ? [{ debugName: "ganttTitleColumnLabel" }] : []));
@@ -335,6 +348,9 @@ class TimelineGantt {
335
348
  defaultVisibleColumns = input(4, ...(ngDevMode ? [{ debugName: "defaultVisibleColumns" }] : []));
336
349
  columnsPaneMinWidthPx = input(0, ...(ngDevMode ? [{ debugName: "columnsPaneMinWidthPx" }] : []));
337
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" }] : []));
338
354
  zoomToFit = input(false, ...(ngDevMode ? [{ debugName: "zoomToFit" }] : []));
339
355
  mappedGanttNodes = input([], ...(ngDevMode ? [{ debugName: "mappedGanttNodes" }] : []));
340
356
  orderedGanttSegments = input([], ...(ngDevMode ? [{ debugName: "orderedGanttSegments" }] : []));
@@ -343,6 +359,7 @@ class TimelineGantt {
343
359
  progressTemplateDirective = input(null, ...(ngDevMode ? [{ debugName: "progressTemplateDirective" }] : []));
344
360
  toggleCollapse = output();
345
361
  progressClick = output();
362
+ timelineViewportWidthChange = output();
346
363
  // Resolves the base columns contract before splitter-specific layout is applied.
347
364
  baseResolvedColumns = computed(() => {
348
365
  const providedColumns = this.columns();
@@ -433,6 +450,36 @@ class TimelineGantt {
433
450
  this.flattenMapped(this.mappedGanttNodes(), 0, segments, firstSeq, lastSeq, this.collapsedGanttIds(), out);
434
451
  return out;
435
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" }] : []));
436
483
  ngAfterViewInit() {
437
484
  this.bindResizeObserver();
438
485
  }
@@ -448,6 +495,24 @@ class TimelineGantt {
448
495
  onRowProgressClick(payload) {
449
496
  this.progressClick.emit(payload);
450
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
+ }
451
516
  onSplitterPointerDown(event) {
452
517
  if (event.button !== 0 || !this.canResizeColumnsPane()) {
453
518
  return;
@@ -517,6 +582,7 @@ class TimelineGantt {
517
582
  return;
518
583
  }
519
584
  this.ganttViewportWidth.set(viewport.clientWidth);
585
+ this.timelineViewportWidthChange.emit(viewport.clientWidth);
520
586
  if (typeof ResizeObserver === 'undefined') {
521
587
  return;
522
588
  }
@@ -526,6 +592,7 @@ class TimelineGantt {
526
592
  this.ganttScrollViewport?.nativeElement.clientWidth ??
527
593
  0;
528
594
  this.ganttViewportWidth.set(width);
595
+ this.timelineViewportWidthChange.emit(width);
529
596
  });
530
597
  this.resizeObserver.observe(viewport);
531
598
  }
@@ -569,6 +636,45 @@ class TimelineGantt {
569
636
  window.removeEventListener('pointerup', this.onSplitterPointerUp);
570
637
  window.removeEventListener('pointercancel', this.onSplitterPointerUp);
571
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
+ }
572
678
  applyColumnsPaneWidth(widthPx) {
573
679
  const clampedWidthPx = this.clampColumnsPaneWidth(widthPx);
574
680
  this.columnsPaneUserWidthPx.set(clampedWidthPx);
@@ -739,7 +845,7 @@ class TimelineGantt {
739
845
  return dir.toLowerCase() === 'rtl';
740
846
  }
741
847
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: TimelineGantt, deps: [], target: i0.ɵɵFactoryTarget.Component });
742
- 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 }, 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" }, 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. -->\r\n<div\r\n class=\"min-h-56 min-w-0 overflow-hidden py-2\"\r\n *transloco=\"let t; prefix: 'timeline'\"\r\n>\r\n <div\r\n class=\"flex min-w-0 border border-surface bg-content\"\r\n #ganttSplitContainer\r\n >\r\n <!-- Fixed metadata columns pane. -->\r\n <div\r\n class=\"shrink-0 overflow-hidden\"\r\n [style.width.px]=\"resolvedColumnsPaneWidthPx()\"\r\n >\r\n <div class=\"mt-timeline-scroll h-full overflow-x-auto overflow-y-hidden\">\r\n <div class=\"min-w-full\" [style.width.px]=\"columnsContentWidthPx()\">\r\n <mt-timeline-gantt-header\r\n [timelineMode]=\"timelineMode()\"\r\n [resolvedColumns]=\"resolvedColumns()\"\r\n [orderedGanttSegments]=\"orderedGanttSegments()\"\r\n [ganttCanvasWidth]=\"0\"\r\n [effectiveGanttSegmentWidthPx]=\"effectiveGanttSegmentWidthPx()\"\r\n [zoomToFit]=\"zoomToFit()\"\r\n [renderTimeline]=\"false\"\r\n />\r\n\r\n <div>\r\n @for (item of resolvedGanttItems(); track $index) {\r\n <mt-timeline-gantt-row\r\n [item]=\"item\"\r\n [resolvedColumns]=\"resolvedColumns()\"\r\n [ganttCanvasWidth]=\"0\"\r\n [laneInsetPx]=\"laneInsetPx()\"\r\n [columnTemplatesByKey]=\"columnTemplatesByKey()\"\r\n [progressTemplateDirective]=\"progressTemplateDirective()\"\r\n [renderTimeline]=\"false\"\r\n (toggleCollapse)=\"onToggleCollapse($event)\"\r\n (progressClick)=\"onRowProgressClick($event)\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n @if (canResizeColumnsPane()) {\r\n <button\r\n type=\"button\"\r\n class=\"mt-timeline-splitter shrink-0\"\r\n [class.mt-timeline-splitter-active]=\"isColumnsResizing()\"\r\n (pointerdown)=\"onSplitterPointerDown($event)\"\r\n (dblclick)=\"onSplitterDoubleClick()\"\r\n (keydown)=\"onSplitterKeyDown($event)\"\r\n [attr.aria-label]=\"t('resize-columns')\"\r\n [title]=\"t('resize-columns-hint')\"\r\n ></button>\r\n }\r\n\r\n <!-- Scrollable timeline pane (horizontal scroll is limited to this area). -->\r\n <div\r\n class=\"mt-timeline-scroll min-w-0 flex-1 overflow-x-auto overflow-y-hidden\"\r\n #ganttScrollViewport\r\n >\r\n <div class=\"min-w-full\" [style.width.px]=\"ganttCanvasWidth()\">\r\n <mt-timeline-gantt-header\r\n [timelineMode]=\"timelineMode()\"\r\n [resolvedColumns]=\"resolvedColumns()\"\r\n [orderedGanttSegments]=\"orderedGanttSegments()\"\r\n [ganttCanvasWidth]=\"ganttCanvasWidth()\"\r\n [effectiveGanttSegmentWidthPx]=\"effectiveGanttSegmentWidthPx()\"\r\n [zoomToFit]=\"zoomToFit()\"\r\n [renderColumns]=\"false\"\r\n />\r\n\r\n <div>\r\n @for (item of resolvedGanttItems(); track $index) {\r\n <mt-timeline-gantt-row\r\n [item]=\"item\"\r\n [resolvedColumns]=\"[]\"\r\n [ganttCanvasWidth]=\"ganttCanvasWidth()\"\r\n [laneInsetPx]=\"laneInsetPx()\"\r\n [columnTemplatesByKey]=\"columnTemplatesByKey()\"\r\n [progressTemplateDirective]=\"progressTemplateDirective()\"\r\n [renderColumns]=\"false\"\r\n (toggleCollapse)=\"onToggleCollapse($event)\"\r\n (progressClick)=\"onRowProgressClick($event)\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\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: "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 });
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 });
743
849
  }
744
850
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: TimelineGantt, decorators: [{
745
851
  type: Component,
@@ -748,14 +854,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
748
854
  TranslocoDirective,
749
855
  TimelineGanttHeader,
750
856
  TimelineGanttRow,
751
- ], template: "<!-- Gantt view shell: scroll viewport + sized timeline table. -->\r\n<div\r\n class=\"min-h-56 min-w-0 overflow-hidden py-2\"\r\n *transloco=\"let t; prefix: 'timeline'\"\r\n>\r\n <div\r\n class=\"flex min-w-0 border border-surface bg-content\"\r\n #ganttSplitContainer\r\n >\r\n <!-- Fixed metadata columns pane. -->\r\n <div\r\n class=\"shrink-0 overflow-hidden\"\r\n [style.width.px]=\"resolvedColumnsPaneWidthPx()\"\r\n >\r\n <div class=\"mt-timeline-scroll h-full overflow-x-auto overflow-y-hidden\">\r\n <div class=\"min-w-full\" [style.width.px]=\"columnsContentWidthPx()\">\r\n <mt-timeline-gantt-header\r\n [timelineMode]=\"timelineMode()\"\r\n [resolvedColumns]=\"resolvedColumns()\"\r\n [orderedGanttSegments]=\"orderedGanttSegments()\"\r\n [ganttCanvasWidth]=\"0\"\r\n [effectiveGanttSegmentWidthPx]=\"effectiveGanttSegmentWidthPx()\"\r\n [zoomToFit]=\"zoomToFit()\"\r\n [renderTimeline]=\"false\"\r\n />\r\n\r\n <div>\r\n @for (item of resolvedGanttItems(); track $index) {\r\n <mt-timeline-gantt-row\r\n [item]=\"item\"\r\n [resolvedColumns]=\"resolvedColumns()\"\r\n [ganttCanvasWidth]=\"0\"\r\n [laneInsetPx]=\"laneInsetPx()\"\r\n [columnTemplatesByKey]=\"columnTemplatesByKey()\"\r\n [progressTemplateDirective]=\"progressTemplateDirective()\"\r\n [renderTimeline]=\"false\"\r\n (toggleCollapse)=\"onToggleCollapse($event)\"\r\n (progressClick)=\"onRowProgressClick($event)\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n @if (canResizeColumnsPane()) {\r\n <button\r\n type=\"button\"\r\n class=\"mt-timeline-splitter shrink-0\"\r\n [class.mt-timeline-splitter-active]=\"isColumnsResizing()\"\r\n (pointerdown)=\"onSplitterPointerDown($event)\"\r\n (dblclick)=\"onSplitterDoubleClick()\"\r\n (keydown)=\"onSplitterKeyDown($event)\"\r\n [attr.aria-label]=\"t('resize-columns')\"\r\n [title]=\"t('resize-columns-hint')\"\r\n ></button>\r\n }\r\n\r\n <!-- Scrollable timeline pane (horizontal scroll is limited to this area). -->\r\n <div\r\n class=\"mt-timeline-scroll min-w-0 flex-1 overflow-x-auto overflow-y-hidden\"\r\n #ganttScrollViewport\r\n >\r\n <div class=\"min-w-full\" [style.width.px]=\"ganttCanvasWidth()\">\r\n <mt-timeline-gantt-header\r\n [timelineMode]=\"timelineMode()\"\r\n [resolvedColumns]=\"resolvedColumns()\"\r\n [orderedGanttSegments]=\"orderedGanttSegments()\"\r\n [ganttCanvasWidth]=\"ganttCanvasWidth()\"\r\n [effectiveGanttSegmentWidthPx]=\"effectiveGanttSegmentWidthPx()\"\r\n [zoomToFit]=\"zoomToFit()\"\r\n [renderColumns]=\"false\"\r\n />\r\n\r\n <div>\r\n @for (item of resolvedGanttItems(); track $index) {\r\n <mt-timeline-gantt-row\r\n [item]=\"item\"\r\n [resolvedColumns]=\"[]\"\r\n [ganttCanvasWidth]=\"ganttCanvasWidth()\"\r\n [laneInsetPx]=\"laneInsetPx()\"\r\n [columnTemplatesByKey]=\"columnTemplatesByKey()\"\r\n [progressTemplateDirective]=\"progressTemplateDirective()\"\r\n [renderColumns]=\"false\"\r\n (toggleCollapse)=\"onToggleCollapse($event)\"\r\n (progressClick)=\"onRowProgressClick($event)\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\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"] }]
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"] }]
752
858
  }], propDecorators: { ganttSplitContainerRef: [{
753
859
  type: ViewChild,
754
860
  args: ['ganttSplitContainer']
755
861
  }], ganttScrollViewportRef: [{
756
862
  type: ViewChild,
757
863
  args: ['ganttScrollViewport']
758
- }], 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 }] }], 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"] }] } });
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"] }] } });
759
877
 
760
878
  /**
761
879
  * Shared timeline header.
@@ -772,31 +890,18 @@ class TimelineHeader {
772
890
  title = input('', ...(ngDevMode ? [{ debugName: "title" }] : []));
773
891
  timelineMode = input('quarterly', ...(ngDevMode ? [{ debugName: "timelineMode" }] : []));
774
892
  timelineModeOptions = input([], ...(ngDevMode ? [{ debugName: "timelineModeOptions" }] : []));
775
- showZoomToFitControl = input(false, ...(ngDevMode ? [{ debugName: "showZoomToFitControl" }] : []));
776
- zoomToFit = input(false, ...(ngDevMode ? [{ debugName: "zoomToFit" }] : []));
777
- zoomToFitDisabled = input(false, ...(ngDevMode ? [{ debugName: "zoomToFitDisabled" }] : []));
778
893
  timelineModeChange = output();
779
- zoomToFitToggle = output();
780
894
  // Re-emits selector changes so parent owns state updates.
781
895
  onTimelineModeChange(mode) {
782
896
  this.timelineModeChange.emit(mode);
783
897
  }
784
- onZoomToFitToggle() {
785
- if (this.zoomToFitDisabled()) {
786
- return;
787
- }
788
- this.zoomToFitToggle.emit();
789
- }
790
- getZoomToFitTooltip() {
791
- return this.zoomToFit() ? 'Disable zoom to fit' : 'Zoom to fit';
792
- }
793
898
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: TimelineHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
794
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.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 }, showZoomToFitControl: { classPropertyName: "showZoomToFitControl", publicName: "showZoomToFitControl", isSignal: true, isRequired: false, transformFunction: null }, zoomToFit: { classPropertyName: "zoomToFit", publicName: "zoomToFit", isSignal: true, isRequired: false, transformFunction: null }, zoomToFitDisabled: { classPropertyName: "zoomToFitDisabled", publicName: "zoomToFitDisabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { timelineModeChange: "timelineModeChange", zoomToFitToggle: "zoomToFitToggle" }, ngImport: i0, template: "<!-- Timeline title + scale mode selector. -->\r\n<div\r\n class=\"flex flex-wrap items-center justify-between gap-3\"\r\n *transloco=\"let t; prefix: 'timeline'\"\r\n>\r\n <div class=\"flex items-center gap-2\">\r\n <mt-icon icon=\"custom.timeline-point\" class=\"text-base\"></mt-icon>\r\n <h3 class=\"text-base font-semibold\">{{ title() || t(\"timeline\") }}</h3>\r\n </div>\r\n\r\n <!-- Scale mode switcher (monthly/quarterly/etc.). -->\r\n <div class=\"flex flex-wrap items-center justify-end gap-2\">\r\n @if (showZoomToFitControl()) {\r\n <mt-button\r\n class=\"shrink-0\"\r\n type=\"button\"\r\n icon=\"layout.maximize-01\"\r\n size=\"large\"\r\n [tooltip]=\"getZoomToFitTooltip()\"\r\n [severity]=\"zoomToFit() ? 'primary' : 'secondary'\"\r\n [outlined]=\"!zoomToFit()\"\r\n [disabled]=\"zoomToFitDisabled()\"\r\n (onClick)=\"onZoomToFitToggle()\"\r\n />\r\n }\r\n\r\n <div class=\"w-44\">\r\n <mt-select-field\r\n [field]=\"false\"\r\n [options]=\"timelineModeOptions()\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [showClear]=\"false\"\r\n [hasPlaceholderPrefix]=\"false\"\r\n [placeholder]=\"t('timeline-mode')\"\r\n [ngModel]=\"timelineMode()\"\r\n (onChange)=\"onTimelineModeChange($event)\"\r\n />\r\n </div>\r\n </div>\r\n</div>\r\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: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { 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 });
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 });
795
900
  }
796
901
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: TimelineHeader, decorators: [{
797
902
  type: Component,
798
- args: [{ selector: 'mt-timeline-header', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [FormsModule, Button, TranslocoDirective, SelectField, Icon], template: "<!-- Timeline title + scale mode selector. -->\r\n<div\r\n class=\"flex flex-wrap items-center justify-between gap-3\"\r\n *transloco=\"let t; prefix: 'timeline'\"\r\n>\r\n <div class=\"flex items-center gap-2\">\r\n <mt-icon icon=\"custom.timeline-point\" class=\"text-base\"></mt-icon>\r\n <h3 class=\"text-base font-semibold\">{{ title() || t(\"timeline\") }}</h3>\r\n </div>\r\n\r\n <!-- Scale mode switcher (monthly/quarterly/etc.). -->\r\n <div class=\"flex flex-wrap items-center justify-end gap-2\">\r\n @if (showZoomToFitControl()) {\r\n <mt-button\r\n class=\"shrink-0\"\r\n type=\"button\"\r\n icon=\"layout.maximize-01\"\r\n size=\"large\"\r\n [tooltip]=\"getZoomToFitTooltip()\"\r\n [severity]=\"zoomToFit() ? 'primary' : 'secondary'\"\r\n [outlined]=\"!zoomToFit()\"\r\n [disabled]=\"zoomToFitDisabled()\"\r\n (onClick)=\"onZoomToFitToggle()\"\r\n />\r\n }\r\n\r\n <div class=\"w-44\">\r\n <mt-select-field\r\n [field]=\"false\"\r\n [options]=\"timelineModeOptions()\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [showClear]=\"false\"\r\n [hasPlaceholderPrefix]=\"false\"\r\n [placeholder]=\"t('timeline-mode')\"\r\n [ngModel]=\"timelineMode()\"\r\n (onChange)=\"onTimelineModeChange($event)\"\r\n />\r\n </div>\r\n </div>\r\n</div>\r\n" }]
799
- }], 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 }] }], showZoomToFitControl: [{ type: i0.Input, args: [{ isSignal: true, alias: "showZoomToFitControl", required: false }] }], zoomToFit: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoomToFit", required: false }] }], zoomToFitDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoomToFitDisabled", required: false }] }], timelineModeChange: [{ type: i0.Output, args: ["timelineModeChange"] }], zoomToFitToggle: [{ type: i0.Output, args: ["zoomToFitToggle"] }] } });
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" }]
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"] }] } });
800
905
 
801
906
  // Default scale mode values (labels are resolved at runtime via i18n).
802
907
  const DEFAULT_TIMELINE_MODE_VALUES = [
@@ -805,6 +910,13 @@ const DEFAULT_TIMELINE_MODE_VALUES = [
805
910
  'biannually',
806
911
  'annually',
807
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
+ };
808
920
  /**
809
921
  * Dynamic Gantt timeline component.
810
922
  *
@@ -827,6 +939,7 @@ class Timeline {
827
939
  transloco = inject(TranslocoService);
828
940
  destroyRef = inject(DestroyRef);
829
941
  activeLang = signal(this.transloco.getActiveLang(), ...(ngDevMode ? [{ debugName: "activeLang" }] : []));
942
+ timelineViewportWidthPx = signal(0, ...(ngDevMode ? [{ debugName: "timelineViewportWidthPx" }] : []));
830
943
  title = input('', ...(ngDevMode ? [{ debugName: "title" }] : []));
831
944
  emptyMessage = input('', ...(ngDevMode ? [{ debugName: "emptyMessage" }] : []));
832
945
  isLoading = input(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
@@ -836,13 +949,29 @@ class Timeline {
836
949
  timelineModeOptions = input([], ...(ngDevMode ? [{ debugName: "timelineModeOptions" }] : []));
837
950
  resolvedTimelineModeOptions = computed(() => {
838
951
  const options = this.timelineModeOptions();
839
- if (options.length)
840
- return options;
841
952
  this.activeLang(); // reactive dependency for language changes
842
- return DEFAULT_TIMELINE_MODE_VALUES.map((value) => ({
843
- label: this.transloco.translate(`timeline.mode-${value}`),
844
- value,
845
- }));
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
+ ];
846
975
  }, ...(ngDevMode ? [{ debugName: "resolvedTimelineModeOptions" }] : []));
847
976
  zoomToFit = model(false, ...(ngDevMode ? [{ debugName: "zoomToFit" }] : []));
848
977
  ganttData = input([], ...(ngDevMode ? [{ debugName: "ganttData" }] : []));
@@ -853,6 +982,9 @@ class Timeline {
853
982
  defaultVisibleColumns = input(4, ...(ngDevMode ? [{ debugName: "defaultVisibleColumns" }] : []));
854
983
  columnsPaneMinWidthPx = input(0, ...(ngDevMode ? [{ debugName: "columnsPaneMinWidthPx" }] : []));
855
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" }] : []));
856
988
  // Backward-compatible defaults when `columns` is null.
857
989
  ganttTitleColumnLabel = input('', ...(ngDevMode ? [{ debugName: "ganttTitleColumnLabel" }] : []));
858
990
  ganttStatusColumnLabel = input('', ...(ngDevMode ? [{ debugName: "ganttStatusColumnLabel" }] : []));
@@ -894,19 +1026,33 @@ class Timeline {
894
1026
  .map((item) => this.mapNode(item, mapping, state))
895
1027
  .filter((item) => item !== null);
896
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" }] : []));
897
1047
  // Builds time columns from the mapped data date range and selected mode.
898
1048
  orderedGanttSegments = computed(() => {
899
- const nodes = this.mappedGanttNodes();
900
- if (!nodes.length) {
901
- return [];
902
- }
903
- const range = this.resolveDateRange(nodes);
1049
+ const range = this.resolvedTimelineRange();
904
1050
  if (!range) {
905
1051
  return [];
906
1052
  }
907
- return this.buildSegments(range.startAt, range.endAt, this.timelineMode());
1053
+ return this.buildSegments(range.startAt, range.endAt, this.resolvedRenderTimelineMode());
908
1054
  }, ...(ngDevMode ? [{ debugName: "orderedGanttSegments" }] : []));
909
- canZoomToFit = computed(() => this.orderedGanttSegments().length > 0, ...(ngDevMode ? [{ debugName: "canZoomToFit" }] : []));
1055
+ isZoomToFitActive = computed(() => this.selectedTimelineView() === ZOOM_TO_FIT_VIEW, ...(ngDevMode ? [{ debugName: "isZoomToFitActive" }] : []));
910
1056
  constructor() {
911
1057
  this.transloco.langChanges$
912
1058
  .pipe(takeUntilDestroyed(this.destroyRef))
@@ -914,17 +1060,19 @@ class Timeline {
914
1060
  }
915
1061
  // Timeline mode is controlled by parent model signal to keep it externally bindable.
916
1062
  onTimelineModeChange(mode) {
917
- if (!mode || this.timelineMode() === mode) {
1063
+ if (!mode || this.selectedTimelineView() === mode) {
918
1064
  return;
919
1065
  }
1066
+ const enableZoomToFit = mode === ZOOM_TO_FIT_VIEW;
1067
+ this.zoomToFit.set(enableZoomToFit);
920
1068
  this.timelineMode.set(mode);
921
1069
  this.timelineModeChangeEvent.emit(mode);
922
1070
  }
923
- onZoomToFitToggle() {
924
- if (!this.canZoomToFit()) {
1071
+ onTimelineViewportWidthChange(widthPx) {
1072
+ if (!Number.isFinite(widthPx) || widthPx <= 0) {
925
1073
  return;
926
1074
  }
927
- this.zoomToFit.update((current) => !current);
1075
+ this.timelineViewportWidthPx.set(widthPx);
928
1076
  }
929
1077
  // Handles row progress clicks: emits public event and opens details popover.
930
1078
  onGanttProgressClick(item, event) {
@@ -1047,10 +1195,24 @@ class Timeline {
1047
1195
  return this.genSegments(startAt, endAt, 6, (month) => `H ${month < 6 ? 1 : 2}`, 'H');
1048
1196
  case 'annually':
1049
1197
  return this.genSegments(startAt, endAt, 12, () => 'Y', 'Y');
1050
- default:
1051
- return this.genSegments(startAt, endAt, 3, (month) => `Q ${Math.floor(month / 3) + 1}`, 'Q');
1052
1198
  }
1053
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
+ }
1054
1216
  // Generates contiguous segments between range boundaries.
1055
1217
  genSegments(startAt, endAt, stepMonths, title, prefix) {
1056
1218
  const segments = [];
@@ -1199,7 +1361,7 @@ class Timeline {
1199
1361
  return anchor;
1200
1362
  }
1201
1363
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: Timeline, deps: [], target: i0.ɵɵFactoryTarget.Component });
1202
- 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 }, 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'\">\r\n <!-- Optional top header (title + timeline mode selector). -->\r\n @if (showHeader()) {\r\n <mt-timeline-header\r\n [title]=\"title() || t('timeline')\"\r\n [timelineMode]=\"timelineMode()\"\r\n [timelineModeOptions]=\"resolvedTimelineModeOptions()\"\r\n [showZoomToFitControl]=\"showZoomToFitControl()\"\r\n [zoomToFit]=\"zoomToFit()\"\r\n [zoomToFitDisabled]=\"!canZoomToFit()\"\r\n (timelineModeChange)=\"onTimelineModeChange($event)\"\r\n (zoomToFitToggle)=\"onZoomToFitToggle()\"\r\n />\r\n }\r\n\r\n <!-- Loading state. -->\r\n @if (isLoading()) {\r\n <div class=\"flex min-h-56 items-center justify-center p-8\">\r\n <div\r\n class=\"h-8 w-8 animate-spin rounded-full border-2 border-surface-200\"\r\n style=\"border-top-color: var(--p-primary-color)\"\r\n ></div>\r\n </div>\r\n } @else if (ganttTemplate(); as template) {\r\n <!-- Full custom gantt-body override. -->\r\n <div class=\"min-h-56 py-4\">\r\n <ng-container [ngTemplateOutlet]=\"template.templateRef\"></ng-container>\r\n </div>\r\n } @else if (\r\n mappedGanttNodes().length > 0 && orderedGanttSegments().length > 0\r\n ) {\r\n <!-- Built-in gantt renderer. -->\r\n <mt-timeline-gantt\r\n [timelineMode]=\"timelineMode()\"\r\n [columns]=\"columns()\"\r\n [ganttTitleColumnLabel]=\"ganttTitleColumnLabel() || t('portfolio-name')\"\r\n [ganttStatusColumnLabel]=\"ganttStatusColumnLabel() || t('status')\"\r\n [ganttInitiativeColumnWidthPx]=\"ganttInitiativeColumnWidthPx()\"\r\n [ganttStatusColumnWidthPx]=\"ganttStatusColumnWidthPx()\"\r\n [showGanttStatusColumn]=\"showGanttStatusColumn()\"\r\n [ganttSegmentWidthPx]=\"ganttSegmentWidthPx()\"\r\n [defaultVisibleColumns]=\"defaultVisibleColumns()\"\r\n [columnsPaneMinWidthPx]=\"columnsPaneMinWidthPx()\"\r\n [columnsPaneMaxWidthPx]=\"columnsPaneMaxWidthPx()\"\r\n [zoomToFit]=\"zoomToFit()\"\r\n [mappedGanttNodes]=\"mappedGanttNodes()\"\r\n [orderedGanttSegments]=\"orderedGanttSegments()\"\r\n [collapsedGanttIds]=\"collapsedGanttIds()\"\r\n [columnTemplatesByKey]=\"columnTemplatesByKey()\"\r\n [progressTemplateDirective]=\"progressTemplate()\"\r\n (toggleCollapse)=\"toggleGanttCollapse($event)\"\r\n (progressClick)=\"onGanttProgressClick($event.item, $event.event)\"\r\n />\r\n } @else {\r\n <!-- Empty state. -->\r\n <div\r\n class=\"flex min-h-56 items-center justify-center p-8 text-sm text-surface-500\"\r\n >\r\n {{ emptyMessage() || t(\"empty-message\") }}\r\n </div>\r\n }\r\n\r\n <!-- Details popover host (custom template or built-in fallback). -->\r\n <p-popover #detailsPopover appendTo=\"body\" (onHide)=\"onDetailsPopoverHide()\">\r\n @if (showGanttDetailsPopup() && selectedGanttItem(); as item) {\r\n @if (popoverTemplate(); as template) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"template.templateRef\"\r\n [ngTemplateOutletContext]=\"getPopoverTemplateContext(item)\"\r\n ></ng-container>\r\n } @else {\r\n <mt-timeline-default-popover\r\n [item]=\"item\"\r\n (requestClose)=\"hideDetailsPopover()\"\r\n />\r\n }\r\n }\r\n </p-popover>\r\n\r\n <!-- Point anchor used to place the popover exactly where user clicked. -->\r\n <span\r\n #detailsPopoverClickAnchor\r\n class=\"pointer-events-none fixed h-0 w-0\"\r\n [style.left.px]=\"-9999\"\r\n [style.top.px]=\"-9999\"\r\n aria-hidden=\"true\"\r\n ></span>\r\n</div>\r\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", "showZoomToFitControl", "zoomToFit", "zoomToFitDisabled"], outputs: ["timelineModeChange", "zoomToFitToggle"] }, { kind: "component", type: TimelineGantt, selector: "mt-timeline-gantt", inputs: ["timelineMode", "columns", "ganttTitleColumnLabel", "ganttStatusColumnLabel", "ganttInitiativeColumnWidthPx", "ganttStatusColumnWidthPx", "showGanttStatusColumn", "ganttSegmentWidthPx", "defaultVisibleColumns", "columnsPaneMinWidthPx", "columnsPaneMaxWidthPx", "zoomToFit", "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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
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 });
1203
1365
  }
1204
1366
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: Timeline, decorators: [{
1205
1367
  type: Component,
@@ -1211,14 +1373,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
1211
1373
  TimelineDefaultPopover,
1212
1374
  Popover,
1213
1375
  NgTemplateOutlet,
1214
- ], host: { class: 'block' }, template: "<div class=\"mt-3 block overflow-hidden\" *transloco=\"let t; prefix: 'timeline'\">\r\n <!-- Optional top header (title + timeline mode selector). -->\r\n @if (showHeader()) {\r\n <mt-timeline-header\r\n [title]=\"title() || t('timeline')\"\r\n [timelineMode]=\"timelineMode()\"\r\n [timelineModeOptions]=\"resolvedTimelineModeOptions()\"\r\n [showZoomToFitControl]=\"showZoomToFitControl()\"\r\n [zoomToFit]=\"zoomToFit()\"\r\n [zoomToFitDisabled]=\"!canZoomToFit()\"\r\n (timelineModeChange)=\"onTimelineModeChange($event)\"\r\n (zoomToFitToggle)=\"onZoomToFitToggle()\"\r\n />\r\n }\r\n\r\n <!-- Loading state. -->\r\n @if (isLoading()) {\r\n <div class=\"flex min-h-56 items-center justify-center p-8\">\r\n <div\r\n class=\"h-8 w-8 animate-spin rounded-full border-2 border-surface-200\"\r\n style=\"border-top-color: var(--p-primary-color)\"\r\n ></div>\r\n </div>\r\n } @else if (ganttTemplate(); as template) {\r\n <!-- Full custom gantt-body override. -->\r\n <div class=\"min-h-56 py-4\">\r\n <ng-container [ngTemplateOutlet]=\"template.templateRef\"></ng-container>\r\n </div>\r\n } @else if (\r\n mappedGanttNodes().length > 0 && orderedGanttSegments().length > 0\r\n ) {\r\n <!-- Built-in gantt renderer. -->\r\n <mt-timeline-gantt\r\n [timelineMode]=\"timelineMode()\"\r\n [columns]=\"columns()\"\r\n [ganttTitleColumnLabel]=\"ganttTitleColumnLabel() || t('portfolio-name')\"\r\n [ganttStatusColumnLabel]=\"ganttStatusColumnLabel() || t('status')\"\r\n [ganttInitiativeColumnWidthPx]=\"ganttInitiativeColumnWidthPx()\"\r\n [ganttStatusColumnWidthPx]=\"ganttStatusColumnWidthPx()\"\r\n [showGanttStatusColumn]=\"showGanttStatusColumn()\"\r\n [ganttSegmentWidthPx]=\"ganttSegmentWidthPx()\"\r\n [defaultVisibleColumns]=\"defaultVisibleColumns()\"\r\n [columnsPaneMinWidthPx]=\"columnsPaneMinWidthPx()\"\r\n [columnsPaneMaxWidthPx]=\"columnsPaneMaxWidthPx()\"\r\n [zoomToFit]=\"zoomToFit()\"\r\n [mappedGanttNodes]=\"mappedGanttNodes()\"\r\n [orderedGanttSegments]=\"orderedGanttSegments()\"\r\n [collapsedGanttIds]=\"collapsedGanttIds()\"\r\n [columnTemplatesByKey]=\"columnTemplatesByKey()\"\r\n [progressTemplateDirective]=\"progressTemplate()\"\r\n (toggleCollapse)=\"toggleGanttCollapse($event)\"\r\n (progressClick)=\"onGanttProgressClick($event.item, $event.event)\"\r\n />\r\n } @else {\r\n <!-- Empty state. -->\r\n <div\r\n class=\"flex min-h-56 items-center justify-center p-8 text-sm text-surface-500\"\r\n >\r\n {{ emptyMessage() || t(\"empty-message\") }}\r\n </div>\r\n }\r\n\r\n <!-- Details popover host (custom template or built-in fallback). -->\r\n <p-popover #detailsPopover appendTo=\"body\" (onHide)=\"onDetailsPopoverHide()\">\r\n @if (showGanttDetailsPopup() && selectedGanttItem(); as item) {\r\n @if (popoverTemplate(); as template) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"template.templateRef\"\r\n [ngTemplateOutletContext]=\"getPopoverTemplateContext(item)\"\r\n ></ng-container>\r\n } @else {\r\n <mt-timeline-default-popover\r\n [item]=\"item\"\r\n (requestClose)=\"hideDetailsPopover()\"\r\n />\r\n }\r\n }\r\n </p-popover>\r\n\r\n <!-- Point anchor used to place the popover exactly where user clicked. -->\r\n <span\r\n #detailsPopoverClickAnchor\r\n class=\"pointer-events-none fixed h-0 w-0\"\r\n [style.left.px]=\"-9999\"\r\n [style.top.px]=\"-9999\"\r\n aria-hidden=\"true\"\r\n ></span>\r\n</div>\r\n", styles: [":host{display:block}\n"] }]
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"] }]
1215
1377
  }], ctorParameters: () => [], propDecorators: { detailsPopover: [{
1216
1378
  type: ViewChild,
1217
1379
  args: ['detailsPopover']
1218
1380
  }], detailsPopoverClickAnchor: [{
1219
1381
  type: ViewChild,
1220
1382
  args: ['detailsPopoverClickAnchor']
1221
- }], 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 }] }], 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 }] }] } });
1222
1384
 
1223
1385
  /**
1224
1386
  * Generated bundle index. Do not edit.