angular-calendar-timeline
Version:
A timeline for angular that shows events on a timeline board in different modes: days, weeks, and months.
1 lines • 126 kB
Source Map (JSON)
{"version":3,"file":"angular-calendar-timeline.mjs","sources":["../../../projects/angular-calendar-timeline/src/lib/helpers/date-helpers.ts","../../../projects/angular-calendar-timeline/src/lib/items-iterator/items-iterator.ts","../../../projects/angular-calendar-timeline/src/lib/zooms-handler/zooms-handler.ts","../../../projects/angular-calendar-timeline/src/lib/models/zoom.ts","../../../projects/angular-calendar-timeline/src/lib/zooms-handler/zooms.ts","../../../projects/angular-calendar-timeline/src/lib/helpers/row-determinant.ts","../../../projects/angular-calendar-timeline/src/lib/helpers/cache.ts","../../../projects/angular-calendar-timeline/src/lib/scale-generator/base-scale-generator.ts","../../../projects/angular-calendar-timeline/src/lib/formatters/day-scale-formatter.ts","../../../projects/angular-calendar-timeline/src/lib/scale-generator/day-scale-generator.ts","../../../projects/angular-calendar-timeline/src/lib/formatters/week-scale-formatter.ts","../../../projects/angular-calendar-timeline/src/lib/scale-generator/week-scale-generator.ts","../../../projects/angular-calendar-timeline/src/lib/formatters/month-scale-formatter.ts","../../../projects/angular-calendar-timeline/src/lib/scale-generator/month-scale-generator.ts","../../../projects/angular-calendar-timeline/src/lib/view-mode-adaptor/base-view-mode-adaptor.ts","../../../projects/angular-calendar-timeline/src/lib/view-mode-adaptor/days-view-mode-adaptor.ts","../../../projects/angular-calendar-timeline/src/lib/view-mode-adaptor/weeks-view-mode-adaptor.ts","../../../projects/angular-calendar-timeline/src/lib/view-mode-adaptor/months-view-mode-adaptor.ts","../../../projects/angular-calendar-timeline/src/lib/strategy-manager.ts","../../../projects/angular-calendar-timeline/src/lib/components/item/timeline-item.component.ts","../../../projects/angular-calendar-timeline/src/lib/components/item/timeline-item.component.html","../../../projects/angular-calendar-timeline/src/lib/components/date-marker/timeline-date-marker.component.ts","../../../projects/angular-calendar-timeline/src/lib/components/date-marker/timeline-date-marker.component.html","../../../projects/angular-calendar-timeline/src/lib/components/scale-header/timeline-scale-header.component.ts","../../../projects/angular-calendar-timeline/src/lib/components/scale-header/timeline-scale-header.component.html","../../../projects/angular-calendar-timeline/src/lib/components/panel/timeline-panel.component.ts","../../../projects/angular-calendar-timeline/src/lib/components/panel/timeline-panel.component.html","../../../projects/angular-calendar-timeline/src/lib/timeline.component.ts","../../../projects/angular-calendar-timeline/src/lib/timeline.component.html","../../../projects/angular-calendar-timeline/src/lib/timeline.module.ts","../../../projects/angular-calendar-timeline/src/public-api.ts","../../../projects/angular-calendar-timeline/src/angular-calendar-timeline.ts"],"sourcesContent":["import { DateInput } from \"../models\";\r\n\r\nexport class DateHelpers {\r\n static generateDateId(date: Date): string {\r\n return `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}-${date.getHours()}-${date.getMinutes()}`;\r\n }\r\n\r\n static lastDayOfMonth(date: DateInput): Date {\r\n const dateWithLastDayOfMonth = new Date(date);\r\n dateWithLastDayOfMonth.setMonth(dateWithLastDayOfMonth.getMonth() + 1);\r\n dateWithLastDayOfMonth.setDate(0);\r\n\r\n return dateWithLastDayOfMonth;\r\n }\r\n\r\n static getDaysInMonth(date: Date): number {\r\n return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();\r\n }\r\n\r\n static firstMondayOfMonth(date: Date): Date {\r\n const firstDay = new Date(new Date(date).setDate(1));\r\n const monday = DateHelpers.firstDayOfWeek(firstDay);\r\n\r\n return monday.getMonth() === date.getMonth() ? monday : new Date(monday.setDate(monday.getDate() + 7));\r\n }\r\n\r\n static firstDayOfWeek(date: DateInput): Date {\r\n date = new Date(date);\r\n const first = date.getDate() - date.getDay() + 1;\r\n\r\n return new Date(new Date(date).setDate(first));\r\n }\r\n\r\n static lastDayOfWeek(date: DateInput): Date {\r\n date = new Date(date);\r\n const dayOfWeek = date.getDay();\r\n const diffToSunday = (dayOfWeek === 0) ? 0 : 7 - dayOfWeek;\r\n date.setDate(date.getDate() + diffToSunday);\r\n\r\n return date;\r\n }\r\n\r\n static dayBeginningTime(day: Date): Date {\r\n day = new Date(day);\r\n day.setHours(0, 0, 0, 0);\r\n\r\n return day;\r\n }\r\n\r\n static dayEndingTime(day: Date): Date {\r\n day = new Date(day);\r\n day.setHours(23, 59, 59, 999);\r\n\r\n return day;\r\n }\r\n}\r\n\r\nexport enum MillisecondsToTime {\r\n Minute = 1000 * 60,\r\n Day = 86400000,\r\n Week = MillisecondsToTime.Day * 7\r\n}\r\n","import { ITimelineItem, IItemsIterator } from \"../models\";\r\n\r\nexport class ItemsIterator implements IItemsIterator {\r\n private _items: ITimelineItem[] = [];\r\n\r\n get items(): ITimelineItem[] {\r\n return this._items;\r\n }\r\n\r\n setItems(items: ITimelineItem[]) {\r\n this._items = items;\r\n this._validate();\r\n this._createItemsLevels();\r\n }\r\n\r\n isEmpty(): boolean {\r\n return !this._items?.length;\r\n }\r\n\r\n getFirstItem(onlyVisible: boolean): ITimelineItem {\r\n let firstItem = null;\r\n\r\n this.forEach((item, parent) => {\r\n if (!item.startDate || !item.endDate) {\r\n return;\r\n }\r\n\r\n if (!firstItem || new Date(firstItem.startDate).getTime() > new Date(item.startDate).getTime()) {\r\n firstItem = item;\r\n }\r\n }, onlyVisible);\r\n\r\n return firstItem;\r\n }\r\n\r\n getLastItem(onlyVisible: boolean): ITimelineItem {\r\n let lastItem = null;\r\n\r\n this.forEach((item, parent) => {\r\n if (!item.startDate || !item.endDate) {\r\n return;\r\n }\r\n\r\n if (!lastItem || new Date(lastItem.endDate).getTime() < new Date(item.endDate).getTime()) {\r\n lastItem = item;\r\n }\r\n }, onlyVisible);\r\n\r\n return lastItem;\r\n }\r\n\r\n forEach(handler: (item: ITimelineItem, parent: (ITimelineItem | null)) => void, onlyVisible = false): void {\r\n function iterateAll(items: ITimelineItem[], parent: ITimelineItem | null): void {\r\n (items ?? []).forEach(item => {\r\n handler(item, parent);\r\n iterateAll(item.streamItems ?? [], item);\r\n if (!onlyVisible || item.childrenItemsExpanded) {\r\n iterateAll(item.childrenItems ?? [], item);\r\n }\r\n });\r\n }\r\n\r\n iterateAll(this._items, null);\r\n }\r\n\r\n private _createItemsLevels(): void {\r\n this.forEach((item, parent) => {\r\n if (item.streamItems) {\r\n item._streamLevels = this._createItemLevels(item);\r\n }\r\n });\r\n }\r\n\r\n private _createItemLevels(item: ITimelineItem): ITimelineItem[][] {\r\n const levels: ITimelineItem[][] = [];\r\n\r\n item.streamItems.forEach(item => {\r\n let isLevelFound = false;\r\n let currentLevelIndex = 0;\r\n while (!isLevelFound) {\r\n\r\n const levelItems = levels[currentLevelIndex];\r\n if (!levelItems) {\r\n levels[currentLevelIndex] = [item];\r\n isLevelFound = true;\r\n break;\r\n }\r\n\r\n const isItemCollides = levelItems.some(levelItem => this._isItemsCollides(levelItem, item));\r\n if (!isItemCollides) {\r\n levels[currentLevelIndex].push(item);\r\n isLevelFound = true;\r\n break;\r\n }\r\n\r\n currentLevelIndex++;\r\n }\r\n })\r\n\r\n return levels;\r\n }\r\n\r\n private _isItemsCollides(item1: ITimelineItem, item2: ITimelineItem): boolean {\r\n const item1Start = item1._left;\r\n const item1End = item1._left + item1._width;\r\n const item2Start = item2._left;\r\n const item2End = item2._left + item2._width;\r\n\r\n return item1Start === item2Start || item1End === item2End ||\r\n item1End > item2Start && item1Start < item2End ||\r\n item2End > item1Start && item2Start < item1End;\r\n }\r\n\r\n private _validate(): void {\r\n this.forEach((item: ITimelineItem) => {\r\n if ((item.startDate && !item.endDate) || (item.endDate && !item.startDate)) {\r\n this._removeItemDates(item);\r\n }\r\n\r\n if (item.streamItems) {\r\n this._removeItemDates(item);\r\n }\r\n });\r\n }\r\n\r\n private _removeItemDates(item: ITimelineItem): void {\r\n delete item.startDate;\r\n delete item.endDate;\r\n }\r\n}\r\n","import { BehaviorSubject, Observable } from \"rxjs\";\r\nimport { ITimelineZoom, TimelineViewMode } from \"../models/zoom\";\r\nimport { IIndexedZoom, IZoomsHandler } from \"../models\";\r\n\r\nexport class ZoomsHandler<ViewMode = TimelineViewMode> implements IZoomsHandler<ViewMode> {\r\n private _zooms: IIndexedZoom<ViewMode>[];\r\n private _activeZoom$ = new BehaviorSubject<IIndexedZoom<ViewMode>>(null);\r\n\r\n activeZoom$: Observable<IIndexedZoom<ViewMode>> = this._activeZoom$.asObservable();\r\n\r\n get activeZoom(): IIndexedZoom<ViewMode> {\r\n return this._activeZoom$.value;\r\n }\r\n\r\n get zooms(): IIndexedZoom<ViewMode>[] {\r\n return this._zooms;\r\n }\r\n\r\n constructor(zooms: ITimelineZoom<ViewMode>[]) {\r\n this.setZooms(zooms);\r\n }\r\n\r\n setZooms(zooms: ITimelineZoom<ViewMode>[]): void {\r\n this._zooms = (zooms ?? []).map((item, index) => ({...item, index}));\r\n this._activeZoom$.next(this.getLastZoom());\r\n }\r\n\r\n getFirstZoom(): IIndexedZoom<ViewMode> {\r\n return this._zooms[0];\r\n }\r\n\r\n getLastZoom(): IIndexedZoom<ViewMode> {\r\n return this._zooms[this._zooms.length - 1];\r\n }\r\n\r\n zoomIn(): void {\r\n let newZoomIndex = this.activeZoom.index + 1;\r\n const lastZoomIndex = this.getLastZoom().index;\r\n if (newZoomIndex > lastZoomIndex) {\r\n newZoomIndex = lastZoomIndex;\r\n }\r\n\r\n this.changeActiveZoom(this._zooms[newZoomIndex]);\r\n }\r\n\r\n zoomOut(): void {\r\n let newZoomIndex = this.activeZoom.index - 1;\r\n const firstZoomIndex = this.getFirstZoom().index;\r\n if (newZoomIndex < firstZoomIndex) {\r\n newZoomIndex = firstZoomIndex;\r\n }\r\n\r\n this.changeActiveZoom(this._zooms[newZoomIndex]);\r\n }\r\n\r\n changeActiveZoom(zoom: ITimelineZoom<ViewMode>): void {\r\n if (zoom) {\r\n this._activeZoom$.next(this._zooms[this._findZoomIndex(zoom)]);\r\n }\r\n }\r\n\r\n isZoomActive(zoom: ITimelineZoom<ViewMode>): boolean {\r\n return this._findZoomIndex(zoom) === this.activeZoom.index;\r\n }\r\n\r\n private _findZoomIndex(zoom: ITimelineZoom<ViewMode>): number {\r\n return this._zooms.findIndex(i => i.columnWidth === zoom.columnWidth && i.viewMode === zoom.viewMode);\r\n }\r\n}\r\n","export enum TimelineViewMode {\r\n Month = 101,\r\n Week = 102,\r\n Day = 103\r\n}\r\n\r\nexport interface ITimelineZoom<ViewMode = TimelineViewMode> {\r\n viewMode: ViewMode;\r\n\r\n columnWidth: number;\r\n}\r\n","import { InjectionToken } from \"@angular/core\";\r\nimport { ITimelineZoom, TimelineViewMode } from \"../models\";\r\n\r\nexport const ZOOMS = new InjectionToken<ITimelineZoom[]>('Zooms');\r\n\r\nexport const DefaultZooms: ITimelineZoom[] = [\r\n {columnWidth: 45, viewMode: TimelineViewMode.Month},\r\n {columnWidth: 60, viewMode: TimelineViewMode.Month},\r\n {columnWidth: 80, viewMode: TimelineViewMode.Month},\r\n {columnWidth: 110, viewMode: TimelineViewMode.Month},\r\n {columnWidth: 140, viewMode: TimelineViewMode.Month},\r\n {columnWidth: 200, viewMode: TimelineViewMode.Month},\r\n {columnWidth: 240, viewMode: TimelineViewMode.Month},\r\n {columnWidth: 60, viewMode: TimelineViewMode.Week},\r\n {columnWidth: 80, viewMode: TimelineViewMode.Week},\r\n {columnWidth: 110, viewMode: TimelineViewMode.Week},\r\n {columnWidth: 140, viewMode: TimelineViewMode.Week},\r\n {columnWidth: 200, viewMode: TimelineViewMode.Week},\r\n {columnWidth: 240, viewMode: TimelineViewMode.Week},\r\n {columnWidth: 45, viewMode: TimelineViewMode.Day},\r\n {columnWidth: 60, viewMode: TimelineViewMode.Day},\r\n {columnWidth: 80, viewMode: TimelineViewMode.Day},\r\n {columnWidth: 110, viewMode: TimelineViewMode.Day},\r\n {columnWidth: 140, viewMode: TimelineViewMode.Day},\r\n {columnWidth: 200, viewMode: TimelineViewMode.Day},\r\n {columnWidth: 240, viewMode: TimelineViewMode.Day},\r\n];\r\n","import { IItemsIterator, ITimelineItem } from \"../models\";\r\n\r\ninterface IRow {\r\n items: ITimelineItem[];\r\n stream: ITimelineItem;\r\n}\r\n\r\nexport class RowDeterminant {\r\n rows: IRow[];\r\n\r\n constructor(private _itemsIterator: IItemsIterator) {\r\n this._generateMap();\r\n }\r\n\r\n private _generateMap(): void {\r\n const map: IRow[] = [];\r\n const iterate = (items: ITimelineItem[]): void => {\r\n (items ?? []).forEach(item => {\r\n if (item.streamItems) {\r\n item._streamLevels.forEach((levelArr, index) => {\r\n map.push({stream: item, items: levelArr})\r\n });\r\n } else {\r\n map.push({stream: item, items: [item]});\r\n }\r\n\r\n if (item.childrenItemsExpanded) {\r\n iterate(item.childrenItems ?? []);\r\n }\r\n });\r\n }\r\n\r\n iterate(this._itemsIterator.items);\r\n this.rows = map;\r\n }\r\n\r\n getRowIndexByItem(item: ITimelineItem): number {\r\n let index;\r\n\r\n for (let i = 0; i < this.rows.length; i++) {\r\n const group = this.rows[i];\r\n\r\n if (item.id === group.stream.id) {\r\n index = i;\r\n break;\r\n }\r\n\r\n const hasChild = group.items.find(i => i.id === item.id);\r\n if (hasChild) {\r\n index = i;\r\n }\r\n }\r\n\r\n return index;\r\n }\r\n\r\n getStreamByRowIndex(index: number): ITimelineItem | undefined {\r\n return this.rows[index]?.stream;\r\n }\r\n}\r\n\r\n","import { DateHelpers } from \"./date-helpers\";\r\n\r\nexport function DatesCacheDecorator(): Function {\r\n return function(target: any, methodName: string, descriptor: PropertyDescriptor) {\r\n if (!target.__datesCache) {\r\n target.__datesCache = new Map<string, unknown>();\r\n }\r\n\r\n const originalMethod = descriptor.value;\r\n\r\n descriptor.value = function(...args: Date[]) {\r\n const cacheKey = `${methodName}-${[...args].map(date => DateHelpers.generateDateId(date)).join('-')}`;\r\n\r\n if (target.__datesCache.has(cacheKey)) {\r\n return target.__datesCache.get(cacheKey);\r\n }\r\n\r\n const result = originalMethod.apply(this, args);\r\n target.__datesCache.set(cacheKey, result);\r\n\r\n return result;\r\n };\r\n };\r\n}\r\n","import {\r\n DateInput,\r\n IItemsIterator,\r\n IScale,\r\n IScaleColumn,\r\n IScaleFormatter,\r\n IScaleGenerator,\r\n IScaleGeneratorConfig,\r\n IScaleGroup\r\n} from \"../models\";\r\nimport { Injectable } from \"@angular/core\";\r\nimport { DateHelpers } from \"../helpers/date-helpers\";\r\nimport { DatesCacheDecorator } from \"../helpers/cache\";\r\n\r\n@Injectable()\r\nexport abstract class BaseScaleGenerator implements IScaleGenerator {\r\n protected _config: IScaleGeneratorConfig;\r\n\r\n public formatter: IScaleFormatter;\r\n\r\n constructor() {\r\n this._config = this._getConfig();\r\n this.formatter = this._config.formatter;\r\n }\r\n\r\n protected abstract _getConfig(): IScaleGeneratorConfig;\r\n\r\n protected abstract _validateStartDate(startDate: DateInput): Date;\r\n\r\n protected abstract _validateEndDate(endDate: DateInput): Date;\r\n\r\n protected abstract _generateGroups(date: Date): IScaleGroup[];\r\n\r\n protected abstract _getColumnIndex(date: Date): number;\r\n\r\n protected abstract _getNextColumnDate(date: Date): Date;\r\n\r\n public getStartDate(itemsBuilder: IItemsIterator): Date {\r\n if (this._config.getStartDate) {\r\n return this._config.getStartDate(itemsBuilder);\r\n }\r\n\r\n const firstItem = itemsBuilder.getFirstItem(false);\r\n const now = Date.now();\r\n const firstItemTime = new Date(firstItem?.startDate ?? now).getTime();\r\n\r\n return this._validateStartDate(firstItemTime < now ? firstItemTime : now);\r\n }\r\n\r\n public getEndDate(itemsBuilder: IItemsIterator): Date {\r\n if (this._config.getEndDate) {\r\n return this._config.getEndDate(itemsBuilder);\r\n }\r\n\r\n const lastItem = itemsBuilder.getLastItem(false);\r\n const now = Date.now();\r\n const lastItemDate = new Date(lastItem?.endDate ?? now);\r\n\r\n return this._validateEndDate(lastItemDate.getTime() < now ? now : lastItemDate);\r\n }\r\n\r\n @DatesCacheDecorator()\r\n generateScale(startDate: Date, endDate: Date): IScale {\r\n let currentDate: Date = new Date(startDate);\r\n const endTime: number = endDate.getTime();\r\n const columns: IScaleColumn[] = [];\r\n while (currentDate.getTime() <= endTime) {\r\n const date = new Date(currentDate);\r\n columns.push({\r\n id: DateHelpers.generateDateId(date),\r\n date: date,\r\n index: this._getColumnIndex(date),\r\n groups: this._generateGroups(date),\r\n });\r\n\r\n currentDate = this._getNextColumnDate(currentDate);\r\n }\r\n\r\n return {\r\n startDate,\r\n endDate,\r\n columns\r\n };\r\n }\r\n}\r\n","import { IScaleColumn, IScaleFormatter, IScaleGroup } from \"../models\";\r\nimport { formatDate } from \"@angular/common\";\r\nimport { Injectable } from \"@angular/core\";\r\n\r\n@Injectable()\r\nexport class DayScaleFormatter implements IScaleFormatter {\r\n formatColumn(column: IScaleColumn, columnWidth: number, locale: string): string {\r\n if (columnWidth < 65)\r\n return formatDate(column.date, 'dd', locale);\r\n\r\n if (columnWidth > 180)\r\n return formatDate(column.date, 'EEEE dd/MM', locale);\r\n\r\n return formatDate(column.date, 'EEE dd/MM', locale);\r\n }\r\n\r\n formatGroup(group: IScaleGroup, locale: string): string {\r\n return formatDate(group.date, 'LLLL', locale);\r\n }\r\n}\r\n","import { BaseScaleGenerator } from './base-scale-generator';\r\nimport { DateInput, IScaleGenerator, IScaleGeneratorConfig, IScaleGroup } from '../models';\r\nimport { DateHelpers } from \"../helpers/date-helpers\";\r\nimport { inject, Injectable, InjectionToken } from \"@angular/core\";\r\nimport { DayScaleFormatter } from \"../formatters/day-scale-formatter\";\r\n\r\nexport const DAY_SCALE_GENERATOR_CONFIG = new InjectionToken<IScaleGeneratorConfig>('Day scale config');\r\n\r\n\r\nconst DefaultConfig: IScaleGeneratorConfig = {\r\n formatter: new DayScaleFormatter(),\r\n}\r\n\r\n@Injectable()\r\nexport class DefaultDayScaleGenerator extends BaseScaleGenerator implements IScaleGenerator {\r\n protected _getConfig(): IScaleGeneratorConfig {\r\n return {...DefaultConfig, ...inject(DAY_SCALE_GENERATOR_CONFIG, {})};\r\n }\r\n\r\n protected _validateStartDate(startDate: DateInput): Date {\r\n const countOfEmptyMonthsBefore = 1;\r\n startDate = new Date(startDate);\r\n startDate.setDate(1);\r\n startDate = DateHelpers.dayBeginningTime(startDate);\r\n startDate.setMonth(startDate.getMonth() - countOfEmptyMonthsBefore);\r\n\r\n return startDate;\r\n }\r\n\r\n protected _validateEndDate(endDate: DateInput): Date {\r\n const countOfEmptyMonthsAfter = 1;\r\n endDate = new Date(endDate);\r\n return new Date(DateHelpers.lastDayOfMonth(endDate).setMonth(endDate.getMonth() + countOfEmptyMonthsAfter));\r\n }\r\n\r\n protected _generateGroups(date: Date): IScaleGroup[] {\r\n date = new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0, 0);\r\n return [{date, id: DateHelpers.generateDateId(date), coverageInPercents: 100}];\r\n }\r\n\r\n protected _getColumnIndex(date: Date): number {\r\n return date.getDate();\r\n }\r\n\r\n protected _getNextColumnDate(date: Date): Date {\r\n return new Date(date.setDate(date.getDate() + 1));\r\n }\r\n}\r\n\r\n@Injectable()\r\nexport class DayScaleGenerator extends DefaultDayScaleGenerator {\r\n}\r\n","import { IScaleColumn, IScaleFormatter, IScaleGroup } from \"../models\";\r\nimport { Injectable } from \"@angular/core\";\r\nimport { formatDate, FormStyle, getLocaleDayNames, TranslationWidth } from \"@angular/common\";\r\n\r\n@Injectable()\r\nexport class WeekScaleFormatter implements IScaleFormatter {\r\n formatColumn(column: IScaleColumn, columnWidth: number, locale: string): string {\r\n if (columnWidth > 100) {\r\n const days = getLocaleDayNames(locale, FormStyle.Format, TranslationWidth.Abbreviated);\r\n\r\n return `${days[1]}-${days[0]} (${column.index})`\r\n }\r\n\r\n return String(column.index);\r\n }\r\n\r\n formatGroup(group: IScaleGroup, locale: string): string {\r\n return formatDate(group.date, 'LLLL y', locale);\r\n }\r\n}\r\n","import { BaseScaleGenerator } from './base-scale-generator';\r\nimport { DateInput, IScaleGenerator, IScaleGeneratorConfig, IScaleGroup } from '../models';\r\nimport { DateHelpers } from \"../helpers/date-helpers\";\r\nimport { WeekScaleFormatter } from \"../formatters/week-scale-formatter\";\r\nimport { inject, Injectable, InjectionToken } from \"@angular/core\";\r\n\r\nexport const WEEK_SCALE_GENERATOR_CONFIG = new InjectionToken<IScaleGeneratorConfig>('Week scale config');\r\n\r\nconst DefaultConfig: IScaleGeneratorConfig = {\r\n formatter: new WeekScaleFormatter(),\r\n}\r\n\r\n@Injectable()\r\nexport class DefaultWeekScaleGenerator extends BaseScaleGenerator implements IScaleGenerator {\r\n protected _getConfig(): IScaleGeneratorConfig {\r\n return {...DefaultConfig, ...inject(WEEK_SCALE_GENERATOR_CONFIG, {})};\r\n }\r\n\r\n protected _validateStartDate(startDate: DateInput): Date {\r\n const countOfEmptyMonthsBefore = 1;\r\n const newDate: Date = new Date(startDate);\r\n newDate.setMonth(newDate.getMonth() - countOfEmptyMonthsBefore);\r\n\r\n return DateHelpers.firstMondayOfMonth(newDate);\r\n }\r\n\r\n protected _validateEndDate(endDate: DateInput): Date {\r\n const countOfEmptyMonthsAfter = 1;\r\n const newDate: Date = new Date(endDate);\r\n newDate.setMonth(newDate.getMonth() + countOfEmptyMonthsAfter);\r\n\r\n return DateHelpers.lastDayOfWeek(newDate);\r\n }\r\n\r\n protected _generateGroups(date: Date): IScaleGroup[] {\r\n const weekStart: Date = DateHelpers.firstDayOfWeek(date);\r\n const weekEnd: Date = DateHelpers.lastDayOfWeek(date);\r\n const weekRelatedToTwoMonths: boolean = weekStart.getMonth() !== weekEnd.getMonth();\r\n\r\n const weekStartGroupDate: Date = new Date(weekStart.getFullYear(), weekStart.getMonth(), 1, 0, 0, 0, 0);\r\n\r\n const groups: IScaleGroup[] = [\r\n {date: weekStartGroupDate, id: DateHelpers.generateDateId(weekStartGroupDate), coverageInPercents: 100}\r\n ];\r\n\r\n if (weekRelatedToTwoMonths) {\r\n groups[0].coverageInPercents = (DateHelpers.getDaysInMonth(weekStart) - (weekStart.getDate() - 1)) / 7 * 100;\r\n\r\n const weekEndGroupDate: Date = new Date(weekEnd.getFullYear(), weekEnd.getMonth(), 1, 0, 0, 0, 0);\r\n\r\n groups.push({\r\n date: weekEndGroupDate,\r\n id: DateHelpers.generateDateId(weekEndGroupDate),\r\n coverageInPercents: 100 - groups[0].coverageInPercents\r\n })\r\n }\r\n\r\n return groups;\r\n }\r\n\r\n protected _getColumnIndex(date: Date): number {\r\n const weekMonday: Date = DateHelpers.firstDayOfWeek(date);\r\n\r\n return Math.ceil(weekMonday.getDate() / 7);\r\n }\r\n\r\n protected _getNextColumnDate(date: Date): Date {\r\n return new Date(date.setDate(date.getDate() + 7));\r\n }\r\n}\r\n\r\n@Injectable()\r\nexport class WeekScaleGenerator extends DefaultWeekScaleGenerator {\r\n}\r\n","import { IScaleColumn, IScaleFormatter, IScaleGroup } from \"../models\";\r\nimport { formatDate } from \"@angular/common\";\r\nimport { Injectable } from \"@angular/core\";\r\n\r\n@Injectable()\r\nexport class MonthScaleFormatter implements IScaleFormatter {\r\n formatColumn(column: IScaleColumn, columnWidth: number, locale: string): string {\r\n if (columnWidth < 65)\r\n return String(column.index);\r\n\r\n if (columnWidth > 180)\r\n return formatDate(column.date, 'LLLL', locale);\r\n\r\n return formatDate(column.date, 'LLL', locale);\r\n }\r\n\r\n formatGroup(group: IScaleGroup, locale: string): string {\r\n return String(group.date.getFullYear());\r\n }\r\n}\r\n","import { BaseScaleGenerator } from './base-scale-generator';\r\nimport { DateInput, IScaleGenerator, IScaleGeneratorConfig, IScaleGroup } from '../models';\r\nimport { DateHelpers } from \"../helpers/date-helpers\";\r\nimport { inject, Injectable, InjectionToken } from \"@angular/core\";\r\nimport { MonthScaleFormatter } from \"../formatters/month-scale-formatter\";\r\n\r\nexport const MONTH_SCALE_GENERATOR_CONFIG = new InjectionToken<IScaleGeneratorConfig>('Month scale config');\r\n\r\nconst DefaultConfig: IScaleGeneratorConfig = {\r\n formatter: new MonthScaleFormatter(),\r\n}\r\n\r\n@Injectable()\r\nexport class DefaultMonthScaleGenerator extends BaseScaleGenerator implements IScaleGenerator {\r\n protected _getConfig(): IScaleGeneratorConfig {\r\n return {...DefaultConfig, ...inject(MONTH_SCALE_GENERATOR_CONFIG, {})};\r\n }\r\n\r\n protected _validateStartDate(startDate: DateInput): Date {\r\n const newDate = new Date(startDate);\r\n const countOfEmptyYearsBefore = 1;\r\n newDate.setDate(1);\r\n newDate.setMonth(0);\r\n newDate.setFullYear(newDate.getFullYear() - countOfEmptyYearsBefore);\r\n\r\n return newDate;\r\n }\r\n\r\n protected _validateEndDate(endDate: DateInput): Date {\r\n const newDate = DateHelpers.lastDayOfMonth(endDate);\r\n const countOfEmptyYearsAfter = 1;\r\n newDate.setMonth(11);\r\n newDate.setFullYear(newDate.getFullYear() + countOfEmptyYearsAfter);\r\n\r\n return newDate;\r\n }\r\n\r\n protected _generateGroups(date: Date): IScaleGroup[] {\r\n date = new Date(date.getFullYear(), 1, 0, 0, 0, 0, 0);\r\n return [{date, id: DateHelpers.generateDateId(date), coverageInPercents: 100}];\r\n }\r\n\r\n protected _getColumnIndex(date: Date): number {\r\n return date.getMonth() + 1;\r\n }\r\n\r\n protected _getNextColumnDate(date: Date): Date {\r\n return new Date(date.setMonth(date.getMonth() + 1));\r\n }\r\n}\r\n\r\n@Injectable()\r\nexport class MonthScaleGenerator extends DefaultMonthScaleGenerator {\r\n}\r\n","import { IViewModeAdaptor } from \"../models\";\r\n\r\nexport abstract class BaseViewModeAdaptor implements IViewModeAdaptor {\r\n abstract getBeginningDateOfColumn(date: Date): Date;\r\n abstract getEndingDateOfColumn(date: Date): Date;\r\n abstract addColumnToDate(date: Date, columns: number): Date;\r\n abstract getUniqueColumnsWithinRange(date: Date, date2: Date): number;\r\n abstract getDurationInColumns(startDate: Date, endDate: Date): number;\r\n\r\n getMiddleDate(startDate: Date, endDate: Date): Date {\r\n const uniqueColumns = this.getUniqueColumnsWithinRange(startDate, endDate);\r\n\r\n return this.addColumnToDate(this.getBeginningDateOfColumn(startDate), uniqueColumns / 2);\r\n }\r\n}\r\n","import { DatesCacheDecorator } from '../helpers/cache';\r\nimport { DateHelpers, MillisecondsToTime } from \"../helpers/date-helpers\";\r\nimport { BaseViewModeAdaptor} from \"./base-view-mode-adaptor\";\r\nimport { IViewModeAdaptor } from \"../models\";\r\n\r\nexport class DaysViewModeAdaptor extends BaseViewModeAdaptor implements IViewModeAdaptor {\r\n @DatesCacheDecorator()\r\n getUniqueColumnsWithinRange(start: Date, end: Date): number {\r\n const startDate = new Date(start.getFullYear(), start.getMonth(), start.getDate());\r\n const endDate = new Date(end.getFullYear(), end.getMonth(), end.getDate());\r\n\r\n return Math.round(Math.abs((startDate.getTime() - endDate.getTime()) / MillisecondsToTime.Day)) + 1;\r\n }\r\n\r\n @DatesCacheDecorator()\r\n getDurationInColumns(startDate: Date, endDate: Date): number {\r\n return Math.abs((startDate.getTime() - endDate.getTime()) / MillisecondsToTime.Day);\r\n }\r\n\r\n addColumnToDate(date: Date, days: number): Date {\r\n const newDate = new Date(date);\r\n newDate.setDate(date.getDate() + days);\r\n newDate.setHours(newDate.getHours() + ((days % 1) * 24));\r\n\r\n return newDate;\r\n }\r\n\r\n getEndingDateOfColumn(date: Date): Date {\r\n return DateHelpers.dayEndingTime(date);\r\n }\r\n\r\n getBeginningDateOfColumn(date: Date): Date {\r\n return DateHelpers.dayBeginningTime(date);\r\n }\r\n}\r\n","import { DatesCacheDecorator } from '../helpers/cache';\r\nimport { DateHelpers, MillisecondsToTime } from \"../helpers/date-helpers\";\r\nimport { BaseViewModeAdaptor} from \"./base-view-mode-adaptor\";\r\nimport { IViewModeAdaptor } from \"../models\";\r\n\r\nexport class WeeksViewModeAdaptor extends BaseViewModeAdaptor implements IViewModeAdaptor {\r\n @DatesCacheDecorator()\r\n getUniqueColumnsWithinRange(start: Date, end: Date): number {\r\n const monday = DateHelpers.firstDayOfWeek(start);\r\n const last = DateHelpers.lastDayOfWeek(end);\r\n\r\n return Math.round(this.getDurationInColumns(monday, last));\r\n }\r\n\r\n @DatesCacheDecorator()\r\n getDurationInColumns(startDate: Date, endDate: Date): number {\r\n return Math.abs((startDate.getTime() - endDate.getTime()) / MillisecondsToTime.Week);\r\n }\r\n\r\n addColumnToDate(date: Date, weeks: number): Date {\r\n const newDate = new Date(date);\r\n newDate.setDate(date.getDate() + (7 * weeks));\r\n newDate.setHours(newDate.getHours() + (((weeks / 7) % 1) * 24));\r\n\r\n return newDate;\r\n }\r\n\r\n getBeginningDateOfColumn(date: Date): Date {\r\n const start = DateHelpers.firstDayOfWeek(new Date(date));\r\n\r\n return DateHelpers.dayBeginningTime(start);\r\n }\r\n\r\n getEndingDateOfColumn(date: Date): Date {\r\n const end = DateHelpers.lastDayOfWeek(new Date(date));\r\n\r\n return DateHelpers.dayEndingTime(end);\r\n }\r\n}\r\n","import { DatesCacheDecorator } from '../helpers/cache';\r\nimport { DateHelpers } from \"../helpers/date-helpers\";\r\nimport { BaseViewModeAdaptor} from \"./base-view-mode-adaptor\";\r\nimport { IViewModeAdaptor } from \"../models\";\r\n\r\nexport class MonthsViewModeAdaptor extends BaseViewModeAdaptor implements IViewModeAdaptor {\r\n getBeginningDateOfColumn(date: Date): Date {\r\n const start = new Date(date);\r\n start.setDate(1);\r\n\r\n return DateHelpers.dayBeginningTime(start);\r\n }\r\n\r\n getEndingDateOfColumn(date: Date): Date {\r\n const end = new Date(date);\r\n end.setDate(DateHelpers.lastDayOfMonth(date).getDate());\r\n\r\n return DateHelpers.dayEndingTime(end);\r\n }\r\n\r\n @DatesCacheDecorator()\r\n getUniqueColumnsWithinRange(startDate: Date, endDate: Date): number {\r\n const diff = this._getCountOfFullMonths(startDate, endDate);\r\n\r\n return (diff < 0 ? 0 : diff) + 1;\r\n }\r\n\r\n @DatesCacheDecorator()\r\n getDurationInColumns(startDate: Date, endDate: Date): number {\r\n const diff = this._getCountOfFullMonths(startDate, endDate);\r\n const firstMonthCompletedPercent = ((startDate.getDate() - 1) + (startDate.getHours() / 24)) / DateHelpers.getDaysInMonth(startDate);\r\n const secondMonthCompletedPercent = ((endDate.getDate() - 1) + (endDate.getHours() / 24)) / DateHelpers.getDaysInMonth(endDate);\r\n\r\n return diff - firstMonthCompletedPercent + secondMonthCompletedPercent;\r\n }\r\n\r\n addColumnToDate(date: Date, months: number): Date {\r\n const newDate = new Date(date);\r\n newDate.setMonth(date.getMonth() + months);\r\n const days = DateHelpers.getDaysInMonth(newDate) * (months % 1);\r\n newDate.setDate(newDate.getDate() + days);\r\n newDate.setHours(newDate.getHours() + ((days % 1) * 24));\r\n\r\n return newDate;\r\n }\r\n\r\n private _getCountOfFullMonths(startDate: Date, endDate: Date): number {\r\n const yearsDiff = endDate.getFullYear() - startDate.getFullYear();\r\n const startMonth = startDate.getMonth();\r\n const endMonth = endDate.getMonth() + (12 * yearsDiff);\r\n\r\n return endMonth - startMonth;\r\n }\r\n}\r\n","import {\r\n IViewModeAdaptor,\r\n IScaleGenerator,\r\n TimelineViewMode\r\n} from \"./models\";\r\nimport { Inject, Injectable } from \"@angular/core\";\r\nimport { DayScaleGenerator } from \"./scale-generator/day-scale-generator\";\r\nimport { WeekScaleGenerator } from \"./scale-generator/week-scale-generator\";\r\nimport { MonthScaleGenerator } from \"./scale-generator/month-scale-generator\";\r\nimport { DaysViewModeAdaptor } from \"./view-mode-adaptor/days-view-mode-adaptor\";\r\nimport { WeeksViewModeAdaptor } from \"./view-mode-adaptor/weeks-view-mode-adaptor\";\r\nimport { MonthsViewModeAdaptor } from \"./view-mode-adaptor/months-view-mode-adaptor\";\r\n\r\nexport interface IStrategyManager<ViewMode = TimelineViewMode> {\r\n getScaleGenerator(viewMode: ViewMode): IScaleGenerator;\r\n\r\n getViewModeAdaptor(viewMode: ViewMode): IViewModeAdaptor;\r\n}\r\n\r\n@Injectable()\r\nexport class DefaultStrategyManager<ViewMode> implements IStrategyManager<ViewMode> {\r\n protected _generatorsDictionary = {\r\n [TimelineViewMode.Day]: this._dayGenerator,\r\n [TimelineViewMode.Week]: this._weekGenerator,\r\n [TimelineViewMode.Month]: this._monthGenerator,\r\n };\r\n\r\n protected _calculatorsDictionary = {\r\n [TimelineViewMode.Day]: new DaysViewModeAdaptor(),\r\n [TimelineViewMode.Week]: new WeeksViewModeAdaptor(),\r\n [TimelineViewMode.Month]: new MonthsViewModeAdaptor(),\r\n };\r\n\r\n constructor(@Inject(DayScaleGenerator) protected _dayGenerator: IScaleGenerator,\r\n @Inject(WeekScaleGenerator) protected _weekGenerator: IScaleGenerator,\r\n @Inject(MonthScaleGenerator) protected _monthGenerator: IScaleGenerator,\r\n ) {\r\n }\r\n\r\n getViewModeAdaptor(viewMode: ViewMode): IViewModeAdaptor {\r\n return this._calculatorsDictionary[viewMode as unknown as TimelineViewMode];\r\n }\r\n\r\n getScaleGenerator(viewMode: ViewMode): IScaleGenerator {\r\n return this._generatorsDictionary[viewMode as unknown as TimelineViewMode];\r\n }\r\n}\r\n\r\n@Injectable()\r\nexport class StrategyManager<ViewMode = TimelineViewMode> extends DefaultStrategyManager<ViewMode> {\r\n}\r\n","import {\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n EventEmitter,\r\n Input,\r\n Output, Renderer2,\r\n TemplateRef\r\n} from '@angular/core';\r\nimport { ResizeEvent } from \"angular-resizable-element\";\r\nimport { DragEndEvent } from \"angular-draggable-droppable/lib/draggable.directive\";\r\nimport { ITimelineItem, IScale } from \"../../models\";\r\n\r\n@Component({\r\n selector: 'timeline-item',\r\n templateUrl: './timeline-item.component.html',\r\n styleUrls: ['./timeline-item.component.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class TimelineItemComponent {\r\n private _item: ITimelineItem;\r\n\r\n private _scale: IScale;\r\n\r\n isInScaleRange = true;\r\n\r\n isItemResizingStarted = false;\r\n\r\n @Input() set item(item: ITimelineItem | undefined) {\r\n this._item = item;\r\n item.updateView = () => this._cdr.detectChanges();\r\n this._checkIsInScaleRange();\r\n };\r\n\r\n @Input() set scale(scale: IScale | undefined) {\r\n this._scale = scale;\r\n this._checkIsInScaleRange();\r\n };\r\n\r\n @Input() rowContainer: HTMLElement;\r\n\r\n @Input() height: number;\r\n\r\n @Input() rowHeight: number;\r\n\r\n @Input() locale: string;\r\n\r\n @Input() contentTemplate: TemplateRef<{ $implicit: ITimelineItem, locale: string }> | undefined;\r\n\r\n @Output() itemResized = new EventEmitter<{ event: ResizeEvent, item: ITimelineItem }>();\r\n\r\n @Output() itemMoved = new EventEmitter<{ event: DragEndEvent, item: ITimelineItem }>();\r\n\r\n get item(): ITimelineItem {\r\n return this._item;\r\n }\r\n\r\n constructor(private _cdr: ChangeDetectorRef,\r\n private _renderer: Renderer2) {\r\n }\r\n\r\n onItemResizeStart(event: ResizeEvent): void {\r\n this.isItemResizingStarted = true;\r\n this._cdr.markForCheck();\r\n }\r\n\r\n onItemResizeEnd(event: ResizeEvent): void {\r\n this.itemResized.emit({event, item: this._item});\r\n setTimeout(() => this.isItemResizingStarted = false);\r\n }\r\n\r\n onItemDragStart(event): void {\r\n this._setRowZIndex(1000);\r\n }\r\n\r\n onItemDropped(event: DragEndEvent): void {\r\n if (!this.isItemResizingStarted) {\r\n this.itemMoved.emit({event, item: this._item});\r\n }\r\n\r\n this._setRowZIndex(1);\r\n }\r\n\r\n private _checkIsInScaleRange(): void {\r\n if (!this._item || !this._scale) {\r\n return;\r\n }\r\n\r\n if (!this._item.startDate || !this._item.endDate) {\r\n this.isInScaleRange = true;\r\n this._cdr.markForCheck();\r\n return;\r\n }\r\n\r\n this.isInScaleRange = this._scale.startDate.getTime() <= this._item.startDate.getTime()\r\n && this._scale.endDate.getTime() >= this._item.endDate.getTime();\r\n this._cdr.markForCheck();\r\n }\r\n\r\n private _setRowZIndex(index: number): void {\r\n this._renderer.setStyle(this.rowContainer, 'z-index', index);\r\n }\r\n}\r\n","<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","import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, TemplateRef } from '@angular/core';\r\nimport { IScale } from \"../../models\";\r\n\r\n@Component({\r\n selector: 'timeline-date-marker',\r\n templateUrl: './timeline-date-marker.component.html',\r\n styleUrls: ['./timeline-date-marker.component.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush\r\n})\r\nexport class TimelineDateMarkerComponent {\r\n isInScaleRange: boolean = true;\r\n\r\n @Input() leftPosition: number = 0;\r\n\r\n @Input() headerHeight: number;\r\n\r\n @Input() customTemplate: TemplateRef<{ leftPosition: number }> | undefined;\r\n\r\n @Input() set scale(scale: IScale) {\r\n this._checkIsInScaleRange(scale);\r\n };\r\n\r\n constructor(private _cdr: ChangeDetectorRef) {\r\n }\r\n\r\n private _checkIsInScaleRange(scale: IScale): void {\r\n const now = Date.now();\r\n this.isInScaleRange = scale.startDate.getTime() <= now && scale.endDate.getTime() >= now;\r\n this._cdr.detectChanges();\r\n }\r\n}\r\n","<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","import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';\r\nimport { IIdObject, ITimelineZoom, IScale, IScaleColumn, IScaleFormatter, IScaleGroup } from \"../../models\";\r\n\r\ninterface IGeneratedGroup {\r\n id: string;\r\n\r\n name: string;\r\n\r\n width: number;\r\n}\r\n\r\n@Component({\r\n selector: 'timeline-scale-header',\r\n templateUrl: 'timeline-scale-header.component.html',\r\n styleUrls: ['timeline-scale-header.component.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class TimelineScaleHeaderComponent implements OnChanges {\r\n @Input() height: number;\r\n\r\n @Input() scale: IScale;\r\n\r\n @Input() formatter: IScaleFormatter;\r\n\r\n @Input() locale: string;\r\n\r\n @Input() zoom: ITimelineZoom<any>;\r\n\r\n public groups: IGeneratedGroup[] = [];\r\n\r\n get columns(): IScaleColumn[] {\r\n return this.scale?.columns ?? [];\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges) {\r\n this._generateGroups();\r\n }\r\n\r\n trackById(index: number, item: IIdObject): number | string {\r\n return item.id;\r\n }\r\n\r\n private _groupColumnGroups(): { [groupId: string]: IScaleGroup[] } {\r\n return this.scale.columns.reduce((groupsMap, column) => {\r\n column.groups.forEach(group => {\r\n groupsMap[group.id] = groupsMap[group.id] ?? [];\r\n groupsMap[group.id].push(group);\r\n });\r\n\r\n return groupsMap;\r\n }, {})\r\n }\r\n\r\n private _generateGroups(): void {\r\n const groupedGroups = this._groupColumnGroups();\r\n\r\n this.groups = Object.keys(groupedGroups).map(groupId => ({\r\n id: groupId,\r\n name: this.formatter.formatGroup(groupedGroups[groupId][0], this.locale),\r\n width: groupedGroups[groupId].reduce((acc, curr) => acc + this.zoom.columnWidth * curr.coverageInPercents / 100, 0)\r\n }));\r\n }\r\n}\r\n\r\n\r\n","<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","import {\r\n ChangeDetectionStrategy, ChangeDetectorRef,\r\n Component,\r\n EventEmitter,\r\n Input,\r\n OnChanges,\r\n Output,\r\n SimpleChanges,\r\n TemplateRef\r\n} from \"@angular/core\";\r\nimport { ResizeEvent } from \"angular-resizable-element\";\r\nimport { ITimelineItem, IIdObject } from \"../../models\";\r\n\r\n@Component({\r\n selector: 'timeline-panel',\r\n templateUrl: 'timeline-panel.component.html',\r\n styleUrls: ['timeline-panel.component.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush\r\n})\r\nexport class TimelinePanelComponent implements OnChanges {\r\n @Input() items: ITimelineItem[] = [];\r\n\r\n @Input() label: string;\r\n\r\n @Input() width: number;\r\n\r\n @Input() resizable: boolean;\r\n\r\n @Input() minWidth: number;\r\n\r\n @Input() maxWidth: number;\r\n\r\n @Input() headerHeight: number;\r\n\r\n @Input() rowHeight: number;\r\n\r\n @Input() locale: string;\r\n\r\n @Input() childGroupOffset: number = 15;\r\n\r\n @Input() itemTemplate: TemplateRef<{ item: ITimelineItem, index: number, depth: number, locale: string }>\r\n\r\n @Output() widthChanged = new EventEmitter<number>();\r\n\r\n constructor(private _cdr: ChangeDetectorRef) {\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges): void {\r\n if (Object.keys(changes).some(key => ['width', 'minWidth', 'maxWidth'].includes(key))) {\r\n this._validateWidth();\r\n }\r\n }\r\n\r\n trackById(index: number, item: IIdObject): number | string {\r\n return item.id;\r\n }\r\n\r\n handleResize(event: ResizeEvent) {\r\n const newWidth = event.rectangle.width;\r\n\r\n if (newWidth < this.minWidth || newWidth > this.maxWidth)\r\n return;\r\n\r\n this.width = newWidth;\r\n this.widthChanged.emit(this.width);\r\n }\r\n\r\n toggleExpand(item: ITimelineItem): void {\r\n item.childrenItemsExpanded = !item.childrenItemsExpanded;\r\n this._cdr.markForCheck();\r\n }\r\n\r\n private _validateWidth(): void {\r\n if (this.width < this.minWidth) {\r\n this.width = this.minWidth;\r\n }\r\n\r\n if (this.width > this.maxWidth) {\r\n this.width = this.maxWidth;\r\n }\r\n }\r\n}\r\n","<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: index, depth: 0}'>\r\n </ng-container>\r\n\r\n <ng-template #itemsIterationTemplate let-item='item' let-index='index' let-depth='depth'>\r\n\r\n <ng-container [ngTemplateOutlet]='itemTemplate || defaultItemTemplate'\r\n [ngTemplateOutletContext]='{item: item, index: index, depth: depth, locale: locale}'>\r\n </ng-container>\r\n \r\n <div *ngIf='item.childrenItemsExpanded'>\r\n <ng-container *ngFor='let innerItem of (item.childrenItems || []); trackBy: trackById; let index = index'\r\n [ngTemplateOutletContext]='{item: innerItem, index: index, depth: depth + 1, locale: locale}'\r\n [ngTemplateOutlet]='itemsIterationTemplate'\r\n ></ng-container>\r\n </div>\r\n </ng-template>\r\n</div>\r\n\r\n<ng-template #defaultItemTemplate let-item='item' let-index='index' let-depth='depth'>\r\n <div [style.height.px]='rowHeight * (item.streamItems ? item?._streamLevels?.length || 1 : 1)'\r\n [style.width.px]='width'\r\n [class.can-collapse]='item.childrenItems && item.childrenItems.length'\r\n class='panel-item'>\r\n\r\n <div [style.marginLeft.px]='depth * childGroupOffset' (click)='toggleExpand(item)' class='item-content'>\r\n <div class='collapse-icon'\r\n [class.collapsed]='!item.childrenItemsExpanded'>\r\n </div>\r\n\r\n <span class='item-name' title=\"{{item.name}}\">{{item.name}}</span>\r\n </div>\r\n </div>\r\n</ng-template>\r\n","import {\r\n AfterViewInit,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n ElementRef,\r\n EventEmitter,\r\n HostListener,\r\n Inject,\r\n Input,\r\n OnDestroy,\r\n Output,\r\n PLATFORM_ID,\r\n TemplateRef,\r\n} from '@angular/core';\r\nimport { ResizeEvent } from 'angular-resizable-element';\r\nimport { interval, Subject, takeUntil } from 'rxjs';\r\nimport { startWith } from 'rxjs/operators';\r\nimport {\r\n IViewModeAdaptor,\r\n IIdObject,\r\n IItemsIterator,\r\n IScale,\r\n IScaleGenerator,\r\n ITimelineItem,\r\n ITimelineZoom, IZoomsHandler, IScaleColumn, IItemTimeChangedEvent, IItemRowChangedEvent, TimelineViewMode\r\n} from './models';\r\nimport { isPlatformBrowser } from \"@angular/common\";\r\nimport { MillisecondsToTime } from \"./helpers/date-helpers\";\r\nimport { ItemsIterator } from \"./items-iterator/items-iterator\";\r\nimport { ZoomsHandler } from \"./zooms-handler/zooms-handler\";\r\nimport { DefaultZooms } from \"./zooms-handler/zooms\";\r\nimport { DragEndEvent } from \"angular-draggable-droppable/lib/draggable.directive\";\r\nimport { StrategyManager } from \"./strategy-manager\";\r\nimport { RowDeterminant } from \"./helpers/row-determinant\";\r\n\r\n@Component({\r\n selector: 'timeline-calendar',\r\n templateUrl: './timeline.component.html',\r\n styleUrls: ['./timeline.component.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class TimelineComponent<ViewMode = TimelineViewMode> implements AfterViewInit, OnDestroy {\r\n /**\r\n * Indicates the current shown date in the middle of user`s screen.\r\n */\r\n public currentDate: Date = new Date();\r\n\r\n /**\r\n * Scale generator changes depending on current view type.\r\n */\r\n public scaleGenerator: IScaleGenerator;\r\n\r\n /**\r\n * View mode adaptor changes depending on current view type.\r\n */\r\n public viewModeAdaptor: IViewModeAdaptor;\r\n\r\n public dateMarkerLeftPosition: number = 0;\r\n\r\n public scale: IScale | undefined;\r\n\r\n public itemsIterator: IItemsIterator = new ItemsIterator();\r\n\r\n public zoomsHandler: IZoomsHandler<ViewMode> = new ZoomsHandler<ViewMode>(DefaultZooms as any);\r\n\r\n private _ignoreNextScrollEvent: boolean = false;\r\n\r\n private _destroy$: Subject<void> = new Subject<void>();\r\n\r\n /**\r\n * Emits event when startDate and endDate of some item was changed by resizing/moving it.\r\n */\r\n @Output() itemTimeChanged: EventEmitter<IItemTimeChangedEvent> = new EventEmitter();\r\n\r\n /**\r\n * Emits event when item was moved by Y axis.\r\n */\r\n @Output() itemRowChanged: EventEmitter<IItemRowChangedEvent> = new EventEmitter();\r\n\r\n /**\r\n * Emi