UNPKG

angular-calendar-timeline

Version:

A timeline for angular that shows events on a timeline board in different modes: days, weeks, and months.

922 lines (899 loc) 88.6 kB
import * as i0 from '@angular/core'; import { InjectionToken, Injectable, inject, Inject, EventEmitter, Component, ChangeDetectionStrategy, Input, Output, ElementRef, PLATFORM_ID, HostListener, NgModule } from '@angular/core'; import * as i1 from '@angular/common'; import { formatDate, getLocaleDayNames, FormStyle, TranslationWidth, isPlatformBrowser, CommonModule } from '@angular/common'; import { BehaviorSubject, Subject, takeUntil, interval } from 'rxjs'; import { startWith } from 'rxjs/operators'; import { __decorate } from 'tslib'; import * as i2 from 'angular-resizable-element'; import { ResizableModule } from 'angular-resizable-element'; import * as i3 from 'angular-draggable-droppable'; import { DragAndDropModule } from 'angular-draggable-droppable'; class DateHelpers { static generateDateId(date) { return `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}-${date.getHours()}-${date.getMinutes()}`; } static lastDayOfMonth(date) { const dateWithLastDayOfMonth = new Date(date); dateWithLastDayOfMonth.setMonth(dateWithLastDayOfMonth.getMonth() + 1); dateWithLastDayOfMonth.setDate(0); return dateWithLastDayOfMonth; } static getDaysInMonth(date) { return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate(); } static firstMondayOfMonth(date) { const firstDay = new Date(new Date(date).setDate(1)); const monday = DateHelpers.firstDayOfWeek(firstDay); return monday.getMonth() === date.getMonth() ? monday : new Date(monday.setDate(monday.getDate() + 7)); } static firstDayOfWeek(date) { date = new Date(date); const first = date.getDate() - date.getDay() + 1; return new Date(new Date(date).setDate(first)); } static lastDayOfWeek(date) { date = new Date(date); const dayOfWeek = date.getDay(); const diffToSunday = (dayOfWeek === 0) ? 0 : 7 - dayOfWeek; date.setDate(date.getDate() + diffToSunday); return date; } static dayBeginningTime(day) { day = new Date(day); day.setHours(0, 0, 0, 0); return day; } static dayEndingTime(day) { day = new Date(day); day.setHours(23, 59, 59, 999); return day; } } var MillisecondsToTime; (function (MillisecondsToTime) { MillisecondsToTime[MillisecondsToTime["Minute"] = 60000] = "Minute"; MillisecondsToTime[MillisecondsToTime["Day"] = 86400000] = "Day"; MillisecondsToTime[MillisecondsToTime["Week"] = 604800000] = "Week"; })(MillisecondsToTime || (MillisecondsToTime = {})); class ItemsIterator { constructor() { this._items = []; } get items() { return this._items; } setItems(items) { this._items = items; this._validate(); this._createItemsLevels(); } isEmpty() { return !this._items?.length; } getFirstItem(onlyVisible) { let firstItem = null; this.forEach((item, parent) => { if (!item.startDate || !item.endDate) { return; } if (!firstItem || new Date(firstItem.startDate).getTime() > new Date(item.startDate).getTime()) { firstItem = item; } }, onlyVisible); return firstItem; } getLastItem(onlyVisible) { let lastItem = null; this.forEach((item, parent) => { if (!item.startDate || !item.endDate) { return; } if (!lastItem || new Date(lastItem.endDate).getTime() < new Date(item.endDate).getTime()) { lastItem = item; } }, onlyVisible); return lastItem; } forEach(handler, onlyVisible = false) { function iterateAll(items, parent) { (items ?? []).forEach(item => { handler(item, parent); iterateAll(item.streamItems ?? [], item); if (!onlyVisible || item.childrenItemsExpanded) { iterateAll(item.childrenItems ?? [], item); } }); } iterateAll(this._items, null); } _createItemsLevels() { this.forEach((item, parent) => { if (item.streamItems) { item._streamLevels = this._createItemLevels(item); } }); } _createItemLevels(item) { const levels = []; item.streamItems.forEach(item => { let isLevelFound = false; let currentLevelIndex = 0; while (!isLevelFound) { const levelItems = levels[currentLevelIndex]; if (!levelItems) { levels[currentLevelIndex] = [item]; isLevelFound = true; break; } const isItemCollides = levelItems.some(levelItem => this._isItemsCollides(levelItem, item)); if (!isItemCollides) { levels[currentLevelIndex].push(item); isLevelFound = true; break; } currentLevelIndex++; } }); return levels; } _isItemsCollides(item1, item2) { const item1Start = item1._left; const item1End = item1._left + item1._width; const item2Start = item2._left; const item2End = item2._left + item2._width; return item1Start === item2Start || item1End === item2End || item1End > item2Start && item1Start < item2End || item2End > item1Start && item2Start < item1End; } _validate() { this.forEach((item) => { if ((item.startDate && !item.endDate) || (item.endDate && !item.startDate)) { this._removeItemDates(item); } if (item.streamItems) { this._removeItemDates(item); } }); } _removeItemDates(item) { delete item.startDate; delete item.endDate; } } class ZoomsHandler { get activeZoom() { return this._activeZoom$.value; } get zooms() { return this._zooms; } constructor(zooms) { this._activeZoom$ = new BehaviorSubject(null); this.activeZoom$ = this._activeZoom$.asObservable(); this.setZooms(zooms); } setZooms(zooms) { this._zooms = (zooms ?? []).map((item, index) => ({ ...item, index })); this._activeZoom$.next(this.getLastZoom()); } getFirstZoom() { return this._zooms[0]; } getLastZoom() { return this._zooms[this._zooms.length - 1]; } zoomIn() { let newZoomIndex = this.activeZoom.index + 1; const lastZoomIndex = this.getLastZoom().index; if (newZoomIndex > lastZoomIndex) { newZoomIndex = lastZoomIndex; } this.changeActiveZoom(this._zooms[newZoomIndex]); } zoomOut() { let newZoomIndex = this.activeZoom.index - 1; const firstZoomIndex = this.getFirstZoom().index; if (newZoomIndex < firstZoomIndex) { newZoomIndex = firstZoomIndex; } this.changeActiveZoom(this._zooms[newZoomIndex]); } changeActiveZoom(zoom) { if (zoom) { this._activeZoom$.next(this._zooms[this._findZoomIndex(zoom)]); } } isZoomActive(zoom) { return this._findZoomIndex(zoom) === this.activeZoom.index; } _findZoomIndex(zoom) { return this._zooms.findIndex(i => i.columnWidth === zoom.columnWidth && i.viewMode === zoom.viewMode); } } var TimelineViewMode; (function (TimelineViewMode) { TimelineViewMode[TimelineViewMode["Month"] = 101] = "Month"; TimelineViewMode[TimelineViewMode["Week"] = 102] = "Week"; TimelineViewMode[TimelineViewMode["Day"] = 103] = "Day"; })(TimelineViewMode || (TimelineViewMode = {})); const ZOOMS = new InjectionToken('Zooms'); const DefaultZooms = [ { columnWidth: 45, viewMode: TimelineViewMode.Month }, { columnWidth: 60, viewMode: TimelineViewMode.Month }, { columnWidth: 80, viewMode: TimelineViewMode.Month }, { columnWidth: 110, viewMode: TimelineViewMode.Month }, { columnWidth: 140, viewMode: TimelineViewMode.Month }, { columnWidth: 200, viewMode: TimelineViewMode.Month }, { columnWidth: 240, viewMode: TimelineViewMode.Month }, { columnWidth: 60, viewMode: TimelineViewMode.Week }, { columnWidth: 80, viewMode: TimelineViewMode.Week }, { columnWidth: 110, viewMode: TimelineViewMode.Week }, { columnWidth: 140, viewMode: TimelineViewMode.Week }, { columnWidth: 200, viewMode: TimelineViewMode.Week }, { columnWidth: 240, viewMode: TimelineViewMode.Week }, { columnWidth: 45, viewMode: TimelineViewMode.Day }, { columnWidth: 60, viewMode: TimelineViewMode.Day }, { columnWidth: 80, viewMode: TimelineViewMode.Day }, { columnWidth: 110, viewMode: TimelineViewMode.Day }, { columnWidth: 140, viewMode: TimelineViewMode.Day }, { columnWidth: 200, viewMode: TimelineViewMode.Day }, { columnWidth: 240, viewMode: TimelineViewMode.Day }, ]; class RowDeterminant { constructor(_itemsIterator) { this._itemsIterator = _itemsIterator; this._generateMap(); } _generateMap() { const map = []; const iterate = (items) => { (items ?? []).forEach(item => { if (item.streamItems) { item._streamLevels.forEach((levelArr, index) => { map.push({ stream: item, items: levelArr }); }); } else { map.push({ stream: item, items: [item] }); } if (item.childrenItemsExpanded) { iterate(item.childrenItems ?? []); } }); }; iterate(this._itemsIterator.items); this.rows = map; } getRowIndexByItem(item) { let index; for (let i = 0; i < this.rows.length; i++) { const group = this.rows[i]; if (item.id === group.stream.id) { index = i; break; } const hasChild = group.items.find(i => i.id === item.id); if (hasChild) { index = i; } } return index; } getStreamByRowIndex(index) { return this.rows[index]?.stream; } } function DatesCacheDecorator() { return function (target, methodName, descriptor) { if (!target.__datesCache) { target.__datesCache = new Map(); } const originalMethod = descriptor.value; descriptor.value = function (...args) { const cacheKey = `${methodName}-${[...args].map(date => DateHelpers.generateDateId(date)).join('-')}`; if (target.__datesCache.has(cacheKey)) { return target.__datesCache.get(cacheKey); } const result = originalMethod.apply(this, args); target.__datesCache.set(cacheKey, result); return result; }; }; } class BaseScaleGenerator { constructor() { this._config = this._getConfig(); this.formatter = this._config.formatter; } getStartDate(itemsBuilder) { if (this._config.getStartDate) { return this._config.getStartDate(itemsBuilder); } const firstItem = itemsBuilder.getFirstItem(false); const now = Date.now(); const firstItemTime = new Date(firstItem?.startDate ?? now).getTime(); return this._validateStartDate(firstItemTime < now ? firstItemTime : now); } getEndDate(itemsBuilder) { if (this._config.getEndDate) { return this._config.getEndDate(itemsBuilder); } const lastItem = itemsBuilder.getLastItem(false); const now = Date.now(); const lastItemDate = new Date(lastItem?.endDate ?? now); return this._validateEndDate(lastItemDate.getTime() < now ? now : lastItemDate); } generateScale(startDate, endDate) { let currentDate = new Date(startDate); const endTime = endDate.getTime(); const columns = []; while (currentDate.getTime() <= endTime) { const date = new Date(currentDate); columns.push({ id: DateHelpers.generateDateId(date), date: date, index: this._getColumnIndex(date), groups: this._generateGroups(date), }); currentDate = this._getNextColumnDate(currentDate); } return { startDate, endDate, columns }; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: BaseScaleGenerator, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: BaseScaleGenerator }); } } __decorate([ DatesCacheDecorator() ], BaseScaleGenerator.prototype, "generateScale", null); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: BaseScaleGenerator, decorators: [{ type: Injectable }], ctorParameters: () => [], propDecorators: { generateScale: [] } }); class DayScaleFormatter { formatColumn(column, columnWidth, locale) { if (columnWidth < 65) return formatDate(column.date, 'dd', locale); if (columnWidth > 180) return formatDate(column.date, 'EEEE dd/MM', locale); return formatDate(column.date, 'EEE dd/MM', locale); } formatGroup(group, locale) { return formatDate(group.date, 'LLLL', locale); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DayScaleFormatter, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DayScaleFormatter }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DayScaleFormatter, decorators: [{ type: Injectable }] }); const DAY_SCALE_GENERATOR_CONFIG = new InjectionToken('Day scale config'); const DefaultConfig$2 = { formatter: new DayScaleFormatter(), }; class DefaultDayScaleGenerator extends BaseScaleGenerator { _getConfig() { return { ...DefaultConfig$2, ...inject(DAY_SCALE_GENERATOR_CONFIG, {}) }; } _validateStartDate(startDate) { const countOfEmptyMonthsBefore = 1; startDate = new Date(startDate); startDate.setDate(1); startDate = DateHelpers.dayBeginningTime(startDate); startDate.setMonth(startDate.getMonth() - countOfEmptyMonthsBefore); return startDate; } _validateEndDate(endDate) { const countOfEmptyMonthsAfter = 1; endDate = new Date(endDate); return new Date(DateHelpers.lastDayOfMonth(endDate).setMonth(endDate.getMonth() + countOfEmptyMonthsAfter)); } _generateGroups(date) { date = new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0, 0); return [{ date, id: DateHelpers.generateDateId(date), coverageInPercents: 100 }]; } _getColumnIndex(date) { return date.getDate(); } _getNextColumnDate(date) { return new Date(date.setDate(date.getDate() + 1)); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DefaultDayScaleGenerator, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DefaultDayScaleGenerator }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DefaultDayScaleGenerator, decorators: [{ type: Injectable }] }); class DayScaleGenerator extends DefaultDayScaleGenerator { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DayScaleGenerator, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DayScaleGenerator }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DayScaleGenerator, decorators: [{ type: Injectable }] }); class WeekScaleFormatter { formatColumn(column, columnWidth, locale) { if (columnWidth > 100) { const days = getLocaleDayNames(locale, FormStyle.Format, TranslationWidth.Abbreviated); return `${days[1]}-${days[0]} (${column.index})`; } return String(column.index); } formatGroup(group, locale) { return formatDate(group.date, 'LLLL y', locale); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WeekScaleFormatter, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WeekScaleFormatter }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WeekScaleFormatter, decorators: [{ type: Injectable }] }); const WEEK_SCALE_GENERATOR_CONFIG = new InjectionToken('Week scale config'); const DefaultConfig$1 = { formatter: new WeekScaleFormatter(), }; class DefaultWeekScaleGenerator extends BaseScaleGenerator { _getConfig() { return { ...DefaultConfig$1, ...inject(WEEK_SCALE_GENERATOR_CONFIG, {}) }; } _validateStartDate(startDate) { const countOfEmptyMonthsBefore = 1; const newDate = new Date(startDate); newDate.setMonth(newDate.getMonth() - countOfEmptyMonthsBefore); return DateHelpers.firstMondayOfMonth(newDate); } _validateEndDate(endDate) { const countOfEmptyMonthsAfter = 1; const newDate = new Date(endDate); newDate.setMonth(newDate.getMonth() + countOfEmptyMonthsAfter); return DateHelpers.lastDayOfWeek(newDate); } _generateGroups(date) { const weekStart = DateHelpers.firstDayOfWeek(date); const weekEnd = DateHelpers.lastDayOfWeek(date); const weekRelatedToTwoMonths = weekStart.getMonth() !== weekEnd.getMonth(); const weekStartGroupDate = new Date(weekStart.getFullYear(), weekStart.getMonth(), 1, 0, 0, 0, 0); const groups = [ { date: weekStartGroupDate, id: DateHelpers.generateDateId(weekStartGroupDate), coverageInPercents: 100 } ]; if (weekRelatedToTwoMonths) { groups[0].coverageInPercents = (DateHelpers.getDaysInMonth(weekStart) - (weekStart.getDate() - 1)) / 7 * 100; const weekEndGroupDate = new Date(weekEnd.getFullYear(), weekEnd.getMonth(), 1, 0, 0, 0, 0); groups.push({ date: weekEndGroupDate, id: DateHelpers.generateDateId(weekEndGroupDate), coverageInPercents: 100 - groups[0].coverageInPercents }); } return groups; } _getColumnIndex(date) { const weekMonday = DateHelpers.firstDayOfWeek(date); return Math.ceil(weekMonday.getDate() / 7); } _getNextColumnDate(date) { return new Date(date.setDate(date.getDate() + 7)); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DefaultWeekScaleGenerator, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DefaultWeekScaleGenerator }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DefaultWeekScaleGenerator, decorators: [{ type: Injectable }] }); class WeekScaleGenerator extends DefaultWeekScaleGenerator { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WeekScaleGenerator, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WeekScaleGenerator }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WeekScaleGenerator, decorators: [{ type: Injectable }] }); class MonthScaleFormatter { formatColumn(column, columnWidth, locale) { if (columnWidth < 65) return String(column.index); if (columnWidth > 180) return formatDate(column.date, 'LLLL', locale); return formatDate(column.date, 'LLL', locale); } formatGroup(group, locale) { return String(group.date.getFullYear()); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MonthScaleFormatter, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MonthScaleFormatter }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MonthScaleFormatter, decorators: [{ type: Injectable }] }); const MONTH_SCALE_GENERATOR_CONFIG = new InjectionToken('Month scale config'); const DefaultConfig = { formatter: new MonthScaleFormatter(), }; class DefaultMonthScaleGenerator extends BaseScaleGenerator { _getConfig() { return { ...DefaultConfig, ...inject(MONTH_SCALE_GENERATOR_CONFIG, {}) }; } _validateStartDate(startDate) { const newDate = new Date(startDate); const countOfEmptyYearsBefore = 1; newDate.setDate(1); newDate.setMonth(0); newDate.setFullYear(newDate.getFullYear() - countOfEmptyYearsBefore); return newDate; } _validateEndDate(endDate) { const newDate = DateHelpers.lastDayOfMonth(endDate); const countOfEmptyYearsAfter = 1; newDate.setMonth(11); newDate.setFullYear(newDate.getFullYear() + countOfEmptyYearsAfter); return newDate; } _generateGroups(date) { date = new Date(date.getFullYear(), 1, 0, 0, 0, 0, 0); return [{ date, id: DateHelpers.generateDateId(date), coverageInPercents: 100 }]; } _getColumnIndex(date) { return date.getMonth() + 1; } _getNextColumnDate(date) { return new Date(date.setMonth(date.getMonth() + 1)); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DefaultMonthScaleGenerator, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DefaultMonthScaleGenerator }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DefaultMonthScaleGenerator, decorators: [{ type: Injectable }] }); class MonthScaleGenerator extends DefaultMonthScaleGenerator { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MonthScaleGenerator, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MonthScaleGenerator }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MonthScaleGenerator, decorators: [{ type: Injectable }] }); class BaseViewModeAdaptor { getMiddleDate(startDate, endDate) { const uniqueColumns = this.getUniqueColumnsWithinRange(startDate, endDate); return this.addColumnToDate(this.getBeginningDateOfColumn(startDate), uniqueColumns / 2); } } class DaysViewModeAdaptor extends BaseViewModeAdaptor { getUniqueColumnsWithinRange(start, end) { const startDate = new Date(start.getFullYear(), start.getMonth(), start.getDate()); const endDate = new Date(end.getFullYear(), end.getMonth(), end.getDate()); return Math.round(Math.abs((startDate.getTime() - endDate.getTime()) / MillisecondsToTime.Day)) + 1; } getDurationInColumns(startDate, endDate) { return Math.abs((startDate.getTime() - endDate.getTime()) / MillisecondsToTime.Day); } addColumnToDate(date, days) { const newDate = new Date(date); newDate.setDate(date.getDate() + days); newDate.setHours(newDate.getHours() + ((days % 1) * 24)); return newDate; } getEndingDateOfColumn(date) { return DateHelpers.dayEndingTime(date); } getBeginningDateOfColumn(date) { return DateHelpers.dayBeginningTime(date); } } __decorate([ DatesCacheDecorator() ], DaysViewModeAdaptor.prototype, "getUniqueColumnsWithinRange", null); __decorate([ DatesCacheDecorator() ], DaysViewModeAdaptor.prototype, "getDurationInColumns", null); class WeeksViewModeAdaptor extends BaseViewModeAdaptor { getUniqueColumnsWithinRange(start, end) { const monday = DateHelpers.firstDayOfWeek(start); const last = DateHelpers.lastDayOfWeek(end); return Math.round(this.getDurationInColumns(monday, last)); } getDurationInColumns(startDate, endDate) { return Math.abs((startDate.getTime() - endDate.getTime()) / MillisecondsToTime.Week); } addColumnToDate(date, weeks) { const newDate = new Date(date); newDate.setDate(date.getDate() + (7 * weeks)); newDate.setHours(newDate.getHours() + (((weeks / 7) % 1) * 24)); return newDate; } getBeginningDateOfColumn(date) { const start = DateHelpers.firstDayOfWeek(new Date(date)); return DateHelpers.dayBeginningTime(start); } getEndingDateOfColumn(date) { const end = DateHelpers.lastDayOfWeek(new Date(date)); return DateHelpers.dayEndingTime(end); } } __decorate([ DatesCacheDecorator() ], WeeksViewModeAdaptor.prototype, "getUniqueColumnsWithinRange", null); __decorate([ DatesCacheDecorator() ], WeeksViewModeAdaptor.prototype, "getDurationInColumns", null); class MonthsViewModeAdaptor extends BaseViewModeAdaptor { getBeginningDateOfColumn(date) { const start = new Date(date); start.setDate(1); return DateHelpers.dayBeginningTime(start); } getEndingDateOfColumn(date) { const end = new Date(date); end.setDate(DateHelpers.lastDayOfMonth(date).getDate()); return DateHelpers.dayEndingTime(end); } getUniqueColumnsWithinRange(startDate, endDate) { const diff = this._getCountOfFullMonths(startDate, endDate); return (diff < 0 ? 0 : diff) + 1; } getDurationInColumns(startDate, endDate) { const diff = this._getCountOfFullMonths(startDate, endDate); const firstMonthCompletedPercent = ((startDate.getDate() - 1) + (startDate.getHours() / 24)) / DateHelpers.getDaysInMonth(startDate); const secondMonthCompletedPercent = ((endDate.getDate() - 1) + (endDate.getHours() / 24)) / DateHelpers.getDaysInMonth(endDate); return diff - firstMonthCompletedPercent + secondMonthCompletedPercent; } addColumnToDate(date, months) { const newDate = new Date(date); newDate.setMonth(date.getMonth() + months); const days = DateHelpers.getDaysInMonth(newDate) * (months % 1); newDate.setDate(newDate.getDate() + days); newDate.setHours(newDate.getHours() + ((days % 1) * 24)); return newDate; } _getCountOfFullMonths(startDate, endDate) { const yearsDiff = endDate.getFullYear() - startDate.getFullYear(); const startMonth = startDate.getMonth(); const endMonth = endDate.getMonth() + (12 * yearsDiff); return endMonth - startMonth; } } __decorate([ DatesCacheDecorator() ], MonthsViewModeAdaptor.prototype, "getUniqueColumnsWithinRange", null); __decorate([ DatesCacheDecorator() ], MonthsViewModeAdaptor.prototype, "getDurationInColumns", null); class DefaultStrategyManager { constructor(_dayGenerator, _weekGenerator, _monthGenerator) { this._dayGenerator = _dayGenerator; this._weekGenerator = _weekGenerator; this._monthGenerator = _monthGenerator; this._generatorsDictionary = { [TimelineViewMode.Day]: this._dayGenerator, [TimelineViewMode.Week]: this._weekGenerator, [TimelineViewMode.Month]: this._monthGenerator, }; this._calculatorsDictionary = { [TimelineViewMode.Day]: new DaysViewModeAdaptor(), [TimelineViewMode.Week]: new WeeksViewModeAdaptor(), [TimelineViewMode.Month]: new MonthsViewModeAdaptor(), }; } getViewModeAdaptor(viewMode) { return this._calculatorsDictionary[viewMode]; } getScaleGenerator(viewMode) { return this._generatorsDictionary[viewMode]; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DefaultStrategyManager, deps: [{ token: DayScaleGenerator }, { token: WeekScaleGenerator }, { token: MonthScaleGenerator }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DefaultStrategyManager }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DefaultStrategyManager, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: undefined, decorators: [{ type: Inject, args: [DayScaleGenerator] }] }, { type: undefined, decorators: [{ type: Inject, args: [WeekScaleGenerator] }] }, { type: undefined, decorators: [{ type: Inject, args: [MonthScaleGenerator] }] }] }); class StrategyManager extends DefaultStrategyManager { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: StrategyManager, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: StrategyManager }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: StrategyManager, decorators: [{ type: Injectable }] }); class TimelineItemComponent { set item(item) { this._item = item; item.updateView = () => this._cdr.detectChanges(); this._checkIsInScaleRange(); } ; set scale(scale) { this._scale = scale; this._checkIsInScaleRange(); } ; get item() { return this._item; } constructor(_cdr, _renderer) { this._cdr = _cdr; this._renderer = _renderer; this.isInScaleRange = true; this.isItemResizingStarted = false; this.itemResized = new EventEmitter(); this.itemMoved = new EventEmitter(); } onItemResizeStart(event) { this.isItemResizingStarted = true; this._cdr.markForCheck(); } onItemResizeEnd(event) { this.itemResized.emit({ event, item: this._item }); setTimeout(() => this.isItemResizingStarted = false); } onItemDragStart(event) { this._setRowZIndex(1000); } onItemDropped(event) { if (!this.isItemResizingStarted) { this.itemMoved.emit({ event, item: this._item }); } this._setRowZIndex(1); } _checkIsInScaleRange() { if (!this._item || !this._scale) { return; } if (!this._item.startDate || !this._item.endDate) { this.isInScaleRange = true; this._cdr.markForCheck(); return; } this.isInScaleRange = this._scale.startDate.getTime() <= this._item.startDate.getTime() && this._scale.endDate.getTime() >= this._item.endDate.getTime(); this._cdr.markForCheck(); } _setRowZIndex(index) { this._renderer.setStyle(this.rowContainer, 'z-index', index); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TimelineItemComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: TimelineItemComponent, selector: "timeline-item", inputs: { item: "item", scale: "scale", rowContainer: "rowContainer", height: "height", rowHeight: "rowHeight", locale: "locale", contentTemplate: "contentTemplate" }, outputs: { itemResized: "itemResized", itemMoved: "itemMoved" }, ngImport: i0, template: "<div class=\"timeline-item\"\r\n *ngIf=\"isInScaleRange\"\r\n ghostElementPositioning='absolute'\r\n mwlResizable\r\n mwlDraggable\r\n [dragActiveClass]=\"'timeline-item_dragging'\"\r\n (dragStart)='onItemDragStart($event)'\r\n (dragEnd)='onItemDropped($event)'\r\n (resizeEnd)='onItemResizeEnd($event)'\r\n (resizeStart)='onItemResizeStart($event)'\r\n [dragAxis]=\"{y: item.canDragY, x: item.canDragX}\"\r\n [enableGhostResize]='true'\r\n [dragSnapGrid]=\"{y: rowHeight}\"\r\n [style.height.px]='height'\r\n [style.left.px]='item?._left'\r\n [style.width.px]='item?._width'>\r\n <div class=\"item-custom-template\" *ngIf=\"contentTemplate; else nameTemplate\">\r\n <ng-container [ngTemplateOutlet]=\"contentTemplate\"\r\n [ngTemplateOutletContext]=\"{$implicit: item, locale: locale}\"\r\n ></ng-container>\r\n </div>\r\n\r\n <ng-template #nameTemplate>\r\n <div class=\"default-content\">\r\n {{item.name}}\r\n </div>\r\n </ng-template>\r\n\r\n <div *ngIf='item.canResizeLeft'\r\n [resizeEdges]='{ left: item.canResizeLeft }'\r\n class='resize-handle-left'\r\n mwlResizeHandle\r\n ></div>\r\n <div *ngIf='item.canResizeRight'\r\n [resizeEdges]='{ right: item.canResizeRight }'\r\n class='resize-handle-right'\r\n mwlResizeHandle\r\n ></div>\r\n</div>\r\n", styles: [".timeline-item{height:100%;position:absolute;box-sizing:border-box;overflow:hidden;top:5px}.item-custom-template{width:100%;height:100%;position:relative;box-sizing:border-box}.default-content{height:100%;width:100%;background-color:#098ed2;padding:2px 10px;display:flex;align-items:center;color:#f1f1f1;box-sizing:border-box;border-radius:2px;cursor:pointer}mwlResizable{box-sizing:border-box}.resize-handle-left,.resize-handle-right{position:absolute;height:100%;cursor:col-resize;width:5px;top:0}.resize-handle-left{left:0}.resize-handle-right{right:0}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.ResizableDirective, selector: "[mwlResizable]", inputs: ["validateResize", "enableGhostResize", "resizeSnapGrid", "resizeCursors", "ghostElementPositioning", "allowNegativeResizes", "mouseMoveThrottleMS"], outputs: ["resizeStart", "resizing", "resizeEnd"], exportAs: ["mwlResizable"] }, { kind: "directive", type: i2.ResizeHandleDirective, selector: "[mwlResizeHandle]", inputs: ["resizeEdges", "resizableContainer"] }, { kind: "directive", type: i3.DraggableDirective, selector: "[mwlDraggable]", inputs: ["dropData", "dragAxis", "dragSnapGrid", "ghostDragEnabled", "showOriginalElementWhileDragging", "validateDrag", "dragCursor", "dragActiveClass", "ghostElementAppendTo", "ghostElementTemplate", "touchStartLongPress", "autoScroll"], outputs: ["dragPointerDown", "dragStart", "ghostElementCreated", "dragging", "dragEnd"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TimelineItemComponent, decorators: [{ type: Component, args: [{ selector: 'timeline-item', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"timeline-item\"\r\n *ngIf=\"isInScaleRange\"\r\n ghostElementPositioning='absolute'\r\n mwlResizable\r\n mwlDraggable\r\n [dragActiveClass]=\"'timeline-item_dragging'\"\r\n (dragStart)='onItemDragStart($event)'\r\n (dragEnd)='onItemDropped($event)'\r\n (resizeEnd)='onItemResizeEnd($event)'\r\n (resizeStart)='onItemResizeStart($event)'\r\n [dragAxis]=\"{y: item.canDragY, x: item.canDragX}\"\r\n [enableGhostResize]='true'\r\n [dragSnapGrid]=\"{y: rowHeight}\"\r\n [style.height.px]='height'\r\n [style.left.px]='item?._left'\r\n [style.width.px]='item?._width'>\r\n <div class=\"item-custom-template\" *ngIf=\"contentTemplate; else nameTemplate\">\r\n <ng-container [ngTemplateOutlet]=\"contentTemplate\"\r\n [ngTemplateOutletContext]=\"{$implicit: item, locale: locale}\"\r\n ></ng-container>\r\n </div>\r\n\r\n <ng-template #nameTemplate>\r\n <div class=\"default-content\">\r\n {{item.name}}\r\n </div>\r\n </ng-template>\r\n\r\n <div *ngIf='item.canResizeLeft'\r\n [resizeEdges]='{ left: item.canResizeLeft }'\r\n class='resize-handle-left'\r\n mwlResizeHandle\r\n ></div>\r\n <div *ngIf='item.canResizeRight'\r\n [resizeEdges]='{ right: item.canResizeRight }'\r\n class='resize-handle-right'\r\n mwlResizeHandle\r\n ></div>\r\n</div>\r\n", styles: [".timeline-item{height:100%;position:absolute;box-sizing:border-box;overflow:hidden;top:5px}.item-custom-template{width:100%;height:100%;position:relative;box-sizing:border-box}.default-content{height:100%;width:100%;background-color:#098ed2;padding:2px 10px;display:flex;align-items:center;color:#f1f1f1;box-sizing:border-box;border-radius:2px;cursor:pointer}mwlResizable{box-sizing:border-box}.resize-handle-left,.resize-handle-right{position:absolute;height:100%;cursor:col-resize;width:5px;top:0}.resize-handle-left{left:0}.resize-handle-right{right:0}\n"] }] }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.Renderer2 }], propDecorators: { item: [{ type: Input }], scale: [{ type: Input }], rowContainer: [{ type: Input }], height: [{ type: Input }], rowHeight: [{ type: Input }], locale: [{ type: Input }], contentTemplate: [{ type: Input }], itemResized: [{ type: Output }], itemMoved: [{ type: Output }] } }); class TimelineDateMarkerComponent { set scale(scale) { this._checkIsInScaleRange(scale); } ; constructor(_cdr) { this._cdr = _cdr; this.isInScaleRange = true; this.leftPosition = 0; } _checkIsInScaleRange(scale) { const now = Date.now(); this.isInScaleRange = scale.startDate.getTime() <= now && scale.endDate.getTime() >= now; this._cdr.detectChanges(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TimelineDateMarkerComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: TimelineDateMarkerComponent, selector: "timeline-date-marker", inputs: { leftPosition: "leftPosition", headerHeight: "headerHeight", customTemplate: "customTemplate", scale: "scale" }, ngImport: i0, template: "<ng-container *ngIf=\"isInScaleRange\">\r\n <ng-container *ngIf=\"customTemplate; else defaultDateMarkerTemplate\"\r\n [ngTemplateOutlet]=\"customTemplate\"\r\n [ngTemplateOutletContext]=\"{leftPosition: leftPosition}\">\r\n </ng-container>\r\n\r\n <ng-template #defaultDateMarkerTemplate>\r\n <div [style.left.px]=\"leftPosition\"\r\n [style.height]=\"'calc(100% - ' + headerHeight + 'px)'\"\r\n class='date-marker'></div>\r\n </ng-template>\r\n</ng-container>\r\n", styles: [".date-marker{position:absolute;width:2px;height:100%;background-color:#2ac226;z-index:2;display:flex;flex-direction:column;transform:translate(-50%)}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TimelineDateMarkerComponent, decorators: [{ type: Component, args: [{ selector: 'timeline-date-marker', changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *ngIf=\"isInScaleRange\">\r\n <ng-container *ngIf=\"customTemplate; else defaultDateMarkerTemplate\"\r\n [ngTemplateOutlet]=\"customTemplate\"\r\n [ngTemplateOutletContext]=\"{leftPosition: leftPosition}\">\r\n </ng-container>\r\n\r\n <ng-template #defaultDateMarkerTemplate>\r\n <div [style.left.px]=\"leftPosition\"\r\n [style.height]=\"'calc(100% - ' + headerHeight + 'px)'\"\r\n class='date-marker'></div>\r\n </ng-template>\r\n</ng-container>\r\n", styles: [".date-marker{position:absolute;width:2px;height:100%;background-color:#2ac226;z-index:2;display:flex;flex-direction:column;transform:translate(-50%)}\n"] }] }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { leftPosition: [{ type: Input }], headerHeight: [{ type: Input }], customTemplate: [{ type: Input }], scale: [{ type: Input }] } }); class TimelineScaleHeaderComponent { constructor() { this.groups = []; } get columns() { return this.scale?.columns ?? []; } ngOnChanges(changes) { this._generateGroups(); } trackById(index, item) { return item.id; } _groupColumnGroups() { return this.scale.columns.reduce((groupsMap, column) => { column.groups.forEach(group => { groupsMap[group.id] = groupsMap[group.id] ?? []; groupsMap[group.id].push(group); }); return groupsMap; }, {}); } _generateGroups() { const groupedGroups = this._groupColumnGroups(); this.groups = Object.keys(groupedGroups).map(groupId => ({ id: groupId, name: this.formatter.formatGroup(groupedGroups[groupId][0], this.locale), width: groupedGroups[groupId].reduce((acc, curr) => acc + this.zoom.columnWidth * curr.coverageInPercents / 100, 0) })); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TimelineScaleHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: TimelineScaleHeaderComponent, selector: "timeline-scale-header", inputs: { height: "height", scale: "scale", formatter: "formatter", locale: "locale", zoom: "zoom" }, usesOnChanges: true, ngImport: i0, template: "<div class='wrapper' [style.height.px]=\"height\">\r\n <div class='groups' *ngIf=\"groups.length\">\r\n <div *ngFor='let group of groups; trackBy: trackById; index as i'\r\n [style.width.px]='group.width' class='group'>\r\n <div>{{group.name}}</div>\r\n </div>\r\n </div>\r\n <div class='columns'>\r\n <div *ngFor='let column of columns; trackBy: trackById'>\r\n <div class='column' [style.width.px]='zoom.columnWidth'>\r\n {{formatter.formatColumn(column, zoom.columnWidth, locale)}}\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n", styles: [":host{z-index:9;position:sticky;top:0;display:flex;flex-direction:row;background:#f8f8f8}.wrapper{display:flex;flex-direction:column}.groups{display:flex;flex-grow:3}.groups .group{height:100%;display:flex;align-items:center;justify-content:center;border-bottom:1px solid #d0d0d0;position:relative;box-sizing:border-box;padding:0 4px}.groups .group:first-child:after{display:none}.groups .group:after{content:\"\";position:absolute;width:1px;height:100%;box-sizing:border-box;left:0;top:0;background:#d0d0d0}.columns{display:flex;flex-grow:2}.columns .column{height:100%;position:relative;display:flex;justify-content:center;align-items:center}.columns .column:after{content:\"\";width:1px;height:100%;left:100%;position:absolute;background:#d0d0d0}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TimelineScaleHeaderComponent, decorators: [{ type: Component, args: [{ selector: 'timeline-scale-header', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class='wrapper' [style.height.px]=\"height\">\r\n <div class='groups' *ngIf=\"groups.length\">\r\n <div *ngFor='let group of groups; trackBy: trackById; index as i'\r\n [style.width.px]='group.width' class='group'>\r\n <div>{{group.name}}</div>\r\n </div>\r\n </div>\r\n <div class='columns'>\r\n <div *ngFor='let column of columns; trackBy: trackById'>\r\n <div class='column' [style.width.px]='zoom.columnWidth'>\r\n {{formatter.formatColumn(column, zoom.columnWidth, locale)}}\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n", styles: [":host{z-index:9;position:sticky;top:0;display:flex;flex-direction:row;background:#f8f8f8}.wrapper{display:flex;flex-direction:column}.groups{display:flex;flex-grow:3}.groups .group{height:100%;display:flex;align-items:center;justify-content:center;border-bottom:1px solid #d0d0d0;position:relative;box-sizing:border-box;padding:0 4px}.groups .group:first-child:after{display:none}.groups .group:after{content:\"\";position:absolute;width:1px;height:100%;box-sizing:border-box;left:0;top:0;background:#d0d0d0}.columns{display:flex;flex-grow:2}.columns .column{height:100%;position:relative;display:flex;justify-content:center;align-items:center}.columns .column:after{content:\"\";width:1px;height:100%;left:100%;position:absolute;background:#d0d0d0}\n"] }] }], propDecorators: { height: [{ type: Input }], scale: [{ type: Input }], formatter: [{ type: Input }], locale: [{ type: Input }], zoom: [{ type: Input }] } }); class TimelinePanelComponent { constructor(_cdr) { this._cdr = _cdr; this.items = []; this.childGroupOffset = 15; this.widthChanged = new EventEmitter(); } ngOnChanges(changes) { if (Object.keys(changes).some(key => ['width', 'minWidth', 'maxWidth'].includes(key))) { this._validateWidth(); } } trackById(index, item) { return item.id; } handleResize(event) { const newWidth = event.rectangle.width; if (newWidth < this.minWidth || newWidth > this.maxWidth) return; this.width = newWidth; this.widthChanged.emit(this.width); } toggleExpand(item) { item.childrenItemsExpanded = !item.childrenItemsExpanded; this._cdr.markForCheck(); } _validateWidth() { if (this.width < this.minWidth) { this.width = this.minWidth; } if (this.width > this.maxWidth) { this.width = this.maxWidth; } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TimelinePanelComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: TimelinePanelComponent, selector: "timeline-panel", inputs: { items: "items", label: "label", width: "width", resizable: "resizable", minWidth: "minWidth", maxWidth: "maxWidth", headerHeight: "headerHeight", rowHeight: "rowHeight", locale: "locale", childGroupOffset: "childGroupOffset", itemTemplate: "itemTemplate" }, outputs: { widthChanged: "widthChanged" }, usesOnChanges: true, ngImport: i0, template: "<div class='panel resize-handle-right'\r\n mwlResizable\r\n (resizing)='handleResize($event)'\r\n [style.width.px]='width'>\r\n\r\n <div mwlResizeHandle [resizeEdges]=\"{right: resizable}\"></div>\r\n\r\n <div class='label' [style.height.px]=\"headerHeight\">{{label}}</div>\r\n\r\n <ng-container *ngFor='let item of items; trackBy: trackById; let index = index'\r\n [ngTemplateOutlet]='itemsIterationTemplate'\r\n [ngTemplateOutletContext]='{item: item, index: ind