@asadi/angular-date-components
Version:
`Angular Date Components` is a comprehensive angular library of date-related components designed to meet the needs of applications that require localization based on various calendar systems. While the package currently includes two powerful components (S
1 lines • 87.6 kB
Source Map (JSON)
{"version":3,"file":"asadi-angular-date-components-core.mjs","sources":["../../../../projects/asadi/angular-date-components/core/src/enum.ts","../../../../projects/asadi/angular-date-components/core/src/injection-token.ts","../../../../projects/asadi/angular-date-components/core/src/components/adcbase-container/adc-base-container.component.ts","../../../../projects/asadi/angular-date-components/core/src/utils/table-cell.tools.ts","../../../../projects/asadi/angular-date-components/core/src/utils/table-view-row.tools.ts","../../../../projects/asadi/angular-date-components/core/src/directives/table-view-controller.directive.ts","../../../../projects/asadi/angular-date-components/core/src/components/adc-table/adc-table.component.ts","../../../../projects/asadi/angular-date-components/core/src/components/adc-table/adc-table.component.html","../../../../projects/asadi/angular-date-components/core/src/services/date-change.service.ts","../../../../projects/asadi/angular-date-components/core/src/services/adc-static-values.ts","../../../../projects/asadi/angular-date-components/core/src/services/common.service.ts","../../../../projects/asadi/angular-date-components/core/src/utils/date-time.tools.ts","../../../../projects/asadi/angular-date-components/core/src/Interactions.ts","../../../../projects/asadi/angular-date-components/core/src/flat-event-builder.ts","../../../../projects/asadi/angular-date-components/core/src/table-selection.ts","../../../../projects/asadi/angular-date-components/core/asadi-angular-date-components-core.ts"],"sourcesContent":["\r\n/**\r\n * Enum representing different date splitters that can be used for formatting dates in Angular Date Components.\r\n * This allows users to specify a preferred delimiter when formatting or parsing dates.\r\n * \r\n * **Values**:\r\n * - `slash ('/')`: Uses a forward slash to separate date components (e.g., `YYYY/MM/DD`).\r\n * - `hyphen ('-')`: Uses a hyphen to separate date components (e.g., `YYYY-MM-DD`).\r\n * \r\n * @example\r\n * // Example usage:\r\n * const dateFormat = ADCDateSplitter.slash;\r\n * console.log(`Selected date format: ${dateFormat}`); // Output: Selected date format: /\r\n */\r\nexport enum ADCDateSplitter\r\n{\r\n slash = '/',\r\n hyphen = '-',\r\n}","import { InjectionToken } from \"@angular/core\";\r\nimport { ADCIDateAdapter, ADCIOptions, ADCIDateFormatter, ADCILabels } from \"./interface\";\r\n\r\n\r\n/**\r\n * Injection token for providing date adapter for `Angular Date Components`.\r\n * Date Adapter is a class that implements `ADCIDateAdapter` interface.\r\n * implement methods of this interface to customize `Angular Date Components` for your calendar type.\r\n */\r\nexport const ADC_DATE_ADAPTER : InjectionToken<ADCIDateAdapter> = new InjectionToken('');\r\n\r\n/**\r\n * Injection token for providing some global options for `Angular Date Components`.\r\n * Use it to do some customization for `Angular Date Components` components.\r\n * It needs a class that implements `ADCIOptions` interface.\r\n */\r\nexport const ADC_OPTIONS: InjectionToken<ADCIOptions> = new InjectionToken('');\r\n\r\n\r\n/**\r\n * Injection token for providing date formats for Angular Date Components.\r\n * Use this token to provide a class that implements `ADCIDateFormatter` interface.\r\n * \r\n * @example\r\n * // Example usage\r\n * \r\n * ```typescript\r\n * export class DateFormatterPersian implements ADCIDateFormatter\r\n {\r\n get DateSplitter(): ADCDateSplitter {\r\n return ADCDateSplitter.slash\r\n }\r\n }\r\n\r\n * @NgModule({\r\n * providers: [\r\n * { provide: ADC_DATE_FORMATTER, useClass: new DateFormatterPersian()}\r\n * ]\r\n * })\r\n * export class AppModule {}\r\n * ```\r\n */\r\nexport const ADC_DATE_FORMATTER: InjectionToken<ADCIDateFormatter> = new InjectionToken('');\r\n\r\n/**\r\n * Injection token for providing custom labels used across Angular Date Components.\r\n * Use this token to override the default labels by supplying an object that implements the `ADCILabels` interface.\r\n * `monthsOfYear` record keys must be exact values of months provided in `ADCIDateAapter` interface\r\n * \r\n * @example\r\n * // Example usage in an Angular module:\r\n * \r\n * ```typescript\r\n * const customLabels: ADCILabels = {\r\n * week: 'هفته',\r\n * year: 'سال',\r\n * day: 'روز',\r\n * today: 'امروز',\r\n * month: 'ماه',\r\n * daysOfWeek: ['یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],\r\n * monthsOfYear: {\r\n * 'January': 'ژانویه',\r\n * 'February': 'فوریه',\r\n * 'March': 'مارس',\r\n * 'April': 'اوریل',\r\n * 'May': 'مه',\r\n * 'June': 'ژوئن',\r\n * 'July': 'ژوئیه',\r\n * 'August': 'اوت',\r\n * 'September': 'سپتامبر',\r\n * 'October': 'اکتبر',\r\n * 'November': 'نوامبر',\r\n * 'December': 'دسامبر'\r\n * }\r\n * };\r\n * \r\n * @NgModule({\r\n * providers: [\r\n * { provide: ADC_LABELS, useValue: customLabels }\r\n * ]\r\n * })\r\n * export class AppModule {}\r\n * ```\r\n */\r\nexport const ADC_LABELS: InjectionToken<ADCILabels> = new InjectionToken('');","import { Component, EventEmitter, Inject, Input, Optional, Output } from '@angular/core';\r\nimport { ADC_LABELS, ADC_OPTIONS } from '../../injection-token';\r\nimport { ADCILabels, ADCIOptions, ADCIToolbar, ADCIViewButton } from '../../interface';\r\nimport { CommonModule } from '@angular/common';\r\n\r\n@Component({\r\n selector: 'adc-base-container',\r\n standalone: true,\r\n imports: [\r\n CommonModule\r\n ],\r\n template: `\r\n <div style=\"border: 1px solid #666;border-radius: 1rem 1rem 0 0;overflow: hidden;\" [dir]=\"direction\">\r\n <div style=\"display: flex;justify-content: space-around;align-items: center;border-bottom: 1px solid #666;padding: 1rem;\">\r\n <div>\r\n <ng-container *ngFor=\"let item of views;let isFirst = first; let isLast = last\">\r\n <button [dir]=\"direction\" class=\"button-group-item\" [class.button-group-item-start]=\"isFirst == true\" [class.button-group-item-end]=\"isLast == true\"\r\n [class.active]=\"currentView == item.id\" (click)=\"onViewChange(item.id)\">{{item.name}}</button>\r\n </ng-container>\r\n </div>\r\n\r\n <div style=\"display: flex;justify-content: space-around;align-items: center;\">\r\n <button (click)=\"onPrevious()\" class=\"adc-icon\" [class.disabled]=\"disablePrevious\">\r\n <i class=\"material-icons\">\r\n {{direction == 'rtl' ? 'chevron_right' : 'chevron_left'}}\r\n </i>\r\n </button>\r\n <button (click)=\"onToday()\" [class.disabled]=\"disableToday\">\r\n {{labels?.today || \"Today\"}}\r\n </button>\r\n <button (click)=\"onNext()\" class=\"adc-icon\" [class.disabled]=\"disableNext\">\r\n <i class=\"material-icons\">\r\n {{direction == 'rtl' ? 'chevron_left' : 'chevron_right'}}\r\n </i>\r\n </button>\r\n </div>\r\n </div>\r\n <div>\r\n <ng-content></ng-content>\r\n </div>\r\n</div>\r\n `,\r\n styleUrls: ['./adc-base-container.component.css']\r\n})\r\nexport class ADCBaseContainerComponent implements ADCIToolbar {\r\n\r\n direction: 'rtl' | 'ltr' = 'rtl';\r\n currentView: string = this.options.initialView;\r\n\r\n private _views: ADCIViewButton[] = [];\r\n\r\n @Input(\"views\")\r\n set views(value: ADCIViewButton[])\r\n {\r\n this._views = value;\r\n\r\n const isViewExist = this._views.filter(v => v.id == this.options.initialView).length != 0;\r\n\r\n if(!isViewExist)\r\n {\r\n this.currentView = this._views[0].id;\r\n this.onViewChangeEvent.emit(this.currentView);\r\n }\r\n }\r\n get views(): ADCIViewButton[]\r\n {\r\n return this._views;\r\n }\r\n\r\n @Input()\r\n disableNext: boolean = false;\r\n\r\n @Input()\r\n disableToday: boolean = false;\r\n\r\n @Input()\r\n disablePrevious: boolean = false;\r\n\r\n @Output('viewChange')\r\n onViewChangeEvent: EventEmitter<string> = new EventEmitter();\r\n\r\n @Output('next')\r\n onNextEvent: EventEmitter<void> = new EventEmitter<void>();\r\n\r\n @Output('today')\r\n onTodayEvent: EventEmitter<void> = new EventEmitter<void>();\r\n\r\n @Output('previous')\r\n onPreviousEvent: EventEmitter<void> = new EventEmitter<void>();\r\n\r\n constructor(\r\n @Optional() @Inject(ADC_LABELS) public labels: ADCILabels | null,\r\n @Inject(ADC_OPTIONS) private options: ADCIOptions,\r\n )\r\n {\r\n this.direction = this.options.direction\r\n }\r\n\r\n onViewChange(view: string): void\r\n {\r\n this.currentView = view;\r\n this.onViewChangeEvent.emit(this.currentView);\r\n }\r\n\r\n onPrevious(): void\r\n {\r\n this.onPreviousEvent.emit();\r\n }\r\n\r\n onToday(): void\r\n {\r\n this.onTodayEvent.emit();\r\n }\r\n\r\n onNext(): void\r\n {\r\n this.onNextEvent.emit();\r\n }\r\n}\r\n","import { ADCITableCell } from \"../interface\";\r\n\r\nexport class ADCTableCell\r\n{\r\n constructor(\r\n public readonly element: HTMLElement\r\n )\r\n {}\r\n\r\n\r\n get rowValue(): string\r\n {\r\n const rowValue = this.element.getAttribute('rowValue');\r\n\r\n if(rowValue == null) throw new Error('table cell does not have any row value');\r\n\r\n return rowValue;\r\n }\r\n\r\n get columnValue(): string\r\n {\r\n const colValue = this.element.getAttribute('columnValue');\r\n\r\n if(colValue == null) throw new Error('table cell does not have any column value');\r\n\r\n return colValue;\r\n }\r\n\r\n get rowIndex(): number\r\n {\r\n const rowIndex = this.element.getAttribute('rowIndex');\r\n\r\n if(rowIndex == null) throw new Error('table cell does not have any row index');\r\n\r\n return +rowIndex;\r\n }\r\n\r\n get columnIndex(): number\r\n {\r\n const columnIndex = this.element.getAttribute('columnIndex');\r\n\r\n if(columnIndex == null) throw new Error('table cell does not have any column index');\r\n\r\n return +columnIndex;\r\n }\r\n\r\n removeState(state: 'selected' | 'invalid' | 'pointed'): void\r\n {\r\n this.element.removeAttribute('cell-selected-range');\r\n\r\n switch(state)\r\n {\r\n case 'selected':\r\n this.element.removeAttribute('selected');\r\n break;\r\n case 'invalid':\r\n this.element.removeAttribute('invalid');\r\n break;\r\n case 'pointed':\r\n this.element.removeAttribute('pointed');\r\n break;\r\n }\r\n }\r\n\r\n resetState(): void\r\n {\r\n this.element.removeAttribute('selected');\r\n this.element.removeAttribute('invalid');\r\n this.element.removeAttribute('pointed');\r\n }\r\n\r\n markAsSelected(): void\r\n {\r\n this.element.setAttribute('selected', '');\r\n }\r\n\r\n markAsInvalid(): void\r\n {\r\n this.element.setAttribute('invalid', '');\r\n }\r\n\r\n markAsPointed(): void\r\n {\r\n this.element.setAttribute('pointed', '');\r\n }\r\n\r\n get invalid(): boolean\r\n {\r\n return this.element.hasAttribute('invalid');\r\n }\r\n\r\n get selected(): boolean\r\n {\r\n return this.element.hasAttribute('selected');\r\n }\r\n\r\n get pointed(): boolean\r\n {\r\n return this.element.hasAttribute('pointed');\r\n }\r\n\r\n asTableCell(): ADCITableCell\r\n {\r\n return {\r\n columnIndex: this.columnIndex,\r\n columnValue: this.columnValue,\r\n rowIndex: this.rowIndex,\r\n rowValue: this.rowValue\r\n }\r\n }\r\n}","import { Renderer2 } from \"@angular/core\";\r\n\r\nexport class ADCTableRowTools\r\n{\r\n constructor(\r\n private renderer: Renderer2,\r\n private element: HTMLElement, \r\n )\r\n {}\r\n\r\n\r\n /**\r\n * a method for setting height of the selected row based on event count\r\n * \r\n * @param eventsCount the number of events\r\n * \r\n * @returns void\r\n */\r\n setHeight(height: string): void\r\n {\r\n this.renderer.setStyle(this.element, \"height\", height);\r\n }\r\n\r\n get cells(): HTMLElement[]\r\n {\r\n const allCells = Array().slice.call(this.cellsContainer.getElementsByClassName('column')) as HTMLElement[];\r\n\r\n return allCells.filter(cell => cell.getAttribute('detail') == 'false');\r\n }\r\n\r\n get horizontalOffset(): number\r\n {\r\n const allCells = Array().slice.call(this.cellsContainer.getElementsByClassName('column')) as HTMLElement[];\r\n\r\n return allCells.filter(cell => cell.getAttribute('detail') == 'true').length == 0 ? 0 : 100;\r\n }\r\n\r\n get attachedEvents(): HTMLElement[]\r\n {\r\n return Array().slice.call(this.eventsContainer.children) as HTMLElement[]\r\n }\r\n\r\n get index(): number\r\n {\r\n const rowIndex = this.element.getAttribute('row-index');\r\n\r\n if(rowIndex == null) throw new Error(\"row index is not available\");\r\n\r\n return +rowIndex;\r\n }\r\n\r\n get isSticky(): boolean\r\n {\r\n return this.element.getAttribute('sticky') == 'true';\r\n }\r\n\r\n get width(): number\r\n {\r\n return (this.element as HTMLElement).scrollWidth - this.horizontalOffset;\r\n }\r\n\r\n attachEvent(event: HTMLElement)\r\n {\r\n this.renderer.appendChild(this.eventsContainer, event);\r\n }\r\n\r\n attachTooltip(tooltipRef: HTMLElement, position: {x: number, y: number}): void\r\n {\r\n tooltipRef.style.visibility = 'hidden';\r\n\r\n this.renderer.appendChild(this.tooltipContainer, tooltipRef);\r\n\r\n const tooltipRect = tooltipRef.getBoundingClientRect();\r\n const containerRect = this.tooltipContainer.getBoundingClientRect();\r\n \r\n // Mouse position relative to the container\r\n const x = position.x - containerRect.left;\r\n const y = position.y - containerRect.top;\r\n\r\n const xAlignment = x >= containerRect.width / 2 ? (tooltipRect.width * -1) : 10;\r\n const yAlignment = y < containerRect.height / 2 ? 10 : (tooltipRect.height * -1);\r\n\r\n tooltipRef.style.left = `${x + xAlignment}px`;\r\n tooltipRef.style.top = `${y + yAlignment}px`;\r\n\r\n tooltipRef.style.visibility = 'visible';\r\n }\r\n\r\n clearEvents(): void\r\n {\r\n this.eventsContainer.innerHTML = '';\r\n }\r\n\r\n clearTooltips(): void\r\n {\r\n this.tooltipContainer.innerHTML = '';\r\n }\r\n\r\n private get cellsContainer(): HTMLElement\r\n {\r\n const children = Array().slice.call(this.element.children) as HTMLElement[];\r\n return children.filter(child => child.getAttribute('cells') != null)[0];\r\n }\r\n\r\n private get eventsContainer(): HTMLElement\r\n {\r\n const children = Array().slice.call(this.element.children) as HTMLElement[];\r\n return children.filter(child => child.getAttribute('events') != null)[0];\r\n }\r\n\r\n private get tooltipContainer(): HTMLElement\r\n {\r\n const children = Array().slice.call(this.element.children) as HTMLElement[];\r\n return children.filter(child => child.getAttribute('tooltip') != null)[0];\r\n }\r\n}","import { Directive, ElementRef, EventEmitter, HostListener, OnDestroy, OnInit, Output, Renderer2 } from '@angular/core';\r\nimport { BehaviorSubject, Observable } from 'rxjs';\r\nimport { ADCITableViewCTRL } from '../interface';\r\nimport { ADCTableCell } from '../utils/table-cell.tools';\r\nimport { ADCTableRowTools } from '../utils/table-view-row.tools';\r\n\r\n@Directive({\r\n selector: '[tableViewController]',\r\n standalone: true,\r\n})\r\nexport class TableViewControllerDirective implements OnInit, ADCITableViewCTRL, OnDestroy{\r\n\r\n private isZoomed = false;\r\n\r\n private childrenChangesObserver!: MutationObserver;\r\n private childrenChangesSubject = new BehaviorSubject<ADCTableCell[]>([]);\r\n\r\n @Output('viewReady')\r\n viewReadyEvent = new EventEmitter<void>();\r\n\r\n\r\n @HostListener('window:keydown', ['$event'])\r\n onZoomChange(event: KeyboardEvent): void\r\n {\r\n if(event.key == 'Shift' && !this.isZoomed)\r\n {\r\n this.isZoomed = true;\r\n this.focusIn();\r\n }\r\n else if(event.key == 'Shift' && this.isZoomed)\r\n {\r\n this.isZoomed = false;\r\n this.focusOut();\r\n }\r\n }\r\n\r\n ngOnInit(): void \r\n {\r\n this.childrenChangesObserver = new MutationObserver((changes) => {\r\n\r\n if(this.scrollListener)\r\n this.scrollListener();\r\n\r\n const tableCells: ADCTableCell[] = [];\r\n\r\n changes.filter(c => c.addedNodes[0] instanceof HTMLElement && c.addedNodes[0].classList.contains('row')).forEach(row => {\r\n const children = Array().slice.call((row.addedNodes[0] as HTMLElement).children) as HTMLElement[];\r\n\r\n const cells = Array().slice.call(children[0].children) as HTMLElement[];\r\n\r\n cells.filter(item => item.getAttribute('selectable') == 'true').forEach(item => tableCells.push(new ADCTableCell(item)));\r\n });\r\n\r\n this.applyScrollingBehavior();\r\n\r\n this.childrenChangesSubject.next(tableCells);\r\n\r\n if(this.isZoomed)\r\n {\r\n this.isZoomed = false;\r\n this.focusOut();\r\n }\r\n\r\n this.viewReadyEvent.emit();\r\n });\r\n\r\n this.childrenChangesObserver.observe(this.container, {childList: true});\r\n }\r\n\r\n private get container(): HTMLElement\r\n {\r\n return this.elRef.nativeElement;\r\n }\r\n\r\n get scrollableContainer(): HTMLElement\r\n {\r\n return this.container.parentElement!;\r\n }\r\n\r\n private scrollListener: (() => void) | null = null;\r\n\r\n constructor(\r\n private renderer: Renderer2,\r\n private elRef: ElementRef\r\n ) {\r\n }\r\n\r\n private applyScrollingBehavior(): void\r\n {\r\n this.scrollListener = this.renderer.listen(this.scrollableContainer, 'scroll', () => {\r\n\r\n if(this.isZoomed)\r\n {\r\n this.scrollableContainer.scrollLeft = 0;\r\n this.scrollableContainer.scrollTop = 0;\r\n\r\n return;\r\n }\r\n\r\n const stickyItems = this.container.querySelectorAll('.row');\r\n\r\n (Array().slice.call(stickyItems) as HTMLElement[]).forEach(item => {\r\n\r\n (Array().slice.call(item.children) as HTMLElement[]).filter(child => child.getAttribute('sticky') == 'true').forEach(stickyChild => {\r\n stickyChild.style.transform = `translateX(-${this.scrollableContainer.scrollLeft}px)`\r\n })\r\n })\r\n\r\n\r\n });\r\n }\r\n\r\n private focusIn(): void\r\n {\r\n const childWidth = this.container.scrollWidth;\r\n const parentWidth = this.scrollableContainer.clientWidth;\r\n\r\n const childHeight = this.container.scrollHeight;\r\n const parentHeight = this.scrollableContainer.clientHeight;\r\n\r\n const scaleX = parentWidth / childWidth * 100;\r\n const scaleY = parentHeight / childHeight * 100;\r\n\r\n this.renderer.setStyle(this.container, 'transform', `scaleX(${scaleX}%) scaleY(${scaleY}%)`);\r\n\r\n requestAnimationFrame(() => {\r\n this.scrollableContainer.scrollLeft = 0;\r\n this.scrollableContainer.scrollTop = 0;\r\n })\r\n }\r\n\r\n private focusOut(): void\r\n {\r\n this.renderer.setStyle(this.container, 'transform', `scaleX(${100}%) scaleY(${100}%)`);\r\n }\r\n\r\n\r\n row(rowIndex: number): ADCTableRowTools \r\n {\r\n const allRows = [].slice.call(this.container.getElementsByClassName(\"row\")) as HTMLElement[];\r\n const row = allRows.filter((r) => r.getAttribute('row-index') == rowIndex.toString())[0];\r\n\r\n return new ADCTableRowTools(this.renderer, row);\r\n }\r\n\r\n cellChanges(): Observable<ADCTableCell[]>\r\n {\r\n return this.childrenChangesSubject.asObservable();\r\n }\r\n\r\n rowsCount(): number \r\n {\r\n return [].slice.call(this.elRef.nativeElement.getElementsByClassName(\"row\")).length;\r\n }\r\n\r\n ngOnDestroy(): void {\r\n if(this.scrollListener) this.scrollListener();\r\n }\r\n\r\n}\r\n","import { CommonModule } from '@angular/common';\r\nimport { AfterViewInit, ChangeDetectionStrategy, Component, EventEmitter, inject, Input, OnInit, Output, Renderer2, ViewChild } from '@angular/core';\r\nimport { ADCITableRow, ADCIOptions } from '../../interface';\r\nimport { TableViewControllerDirective } from '../../directives/table-view-controller.directive';\r\nimport { ADC_OPTIONS } from '../../injection-token';\r\nimport { TableSelection } from '../../table-selection';\r\nimport { FlatEventBuilder } from '../../flat-event-builder';\r\n\r\n/**\r\n * A reusable and customizable table component for displaying date-related data.\r\n * The `adc-table` component is designed for use within the Angular Date Components package.\r\n * It supports features like row and column labels, custom cell event builders, and date range selection.\r\n *\r\n * The component allows for displaying a dynamic table with custom rows, columns, and cells.\r\n * It supports both left-to-right (`ltr`) and right-to-left (`rtl`) layouts.\r\n * \r\n * ### Features\r\n * - Dynamically updates the table view when cell data changes.\r\n * - Supports date filtering and custom event builders for handling cell events.\r\n * - Emits an event when a date range is selected.\r\n *\r\n * @example\r\n * ```html\r\n * <adc-table\r\n * [Rows]=\"tableRows\"\r\n * [Columns]=\"tableColumns\"\r\n * [Cells]=\"tableCells\"\r\n * [Title]=\"'My Schedule'\"\r\n * [TitleClass]=\"'table-title'\"\r\n * [ShowRowLabels]=\"true\"\r\n * [ShowColumnLabels]=\"false\"\r\n * [Dir]=\"'ltr'\"\r\n * [DateFilter]=\"customDateFilter\"\r\n * [EventBuilder]=\"eventBuilderInstance\"\r\n * (DateRangeSelect)=\"onDateRangeSelect($event)\">\r\n * </adc-table>\r\n * ```\r\n * \r\n * @example\r\n * ```typescript\r\n * // In your component class\r\n * tableRows: ADCITableRow[] = [...];\r\n * tableColumns: ADCITableColumn[] = [...];\r\n * tableCells: ADCITableCell[] = [...];\r\n * \r\n * onDateRangeSelect(selectedCells: ADCITableCell[]) {\r\n * console.log('Selected cells:', selectedCells);\r\n * }\r\n * ```\r\n */\r\n@Component({\r\n selector: 'adc-table',\r\n standalone: true,\r\n imports: [\r\n CommonModule,\r\n TableViewControllerDirective,\r\n ],\r\n templateUrl: './adc-table.component.html',\r\n styleUrls: ['./adc-table.component.css'],\r\n changeDetection: ChangeDetectionStrategy.OnPush\r\n})\r\nexport class ADCTableComponent implements AfterViewInit, OnInit{\r\n\r\n readonly options = inject<ADCIOptions>(ADC_OPTIONS);\r\n readonly renderer = inject(Renderer2);\r\n\r\n /**\r\n * An array of rows to display in the table.\r\n */\r\n @Input('rows')\r\n tableRows: ADCITableRow[] = [];\r\n\r\n /**\r\n * a boolean which determines if row details should be displayed or not\r\n */\r\n @Input('showRowDetails')\r\n showRow: boolean = true;\r\n\r\n /**\r\n * \r\n * The title displayed above the table.\r\n */\r\n @Input('title')\r\n title: string = '';\r\n\r\n /**\r\n * CSS classes to apply to the title.\r\n */\r\n @Input('titleClass')\r\n titleClassList: string = '';\r\n\r\n @Input(\"eventBuilder\")\r\n eventBuilder!: FlatEventBuilder;\r\n\r\n @Input('selectionManager')\r\n selectionManager!: TableSelection;\r\n\r\n @ViewChild(TableViewControllerDirective)\r\n private viewCtrl!: TableViewControllerDirective;\r\n\r\n @Output('viewReady')\r\n viewReadyEvent = new EventEmitter<void>();\r\n\r\n maxHeight: string | null = null;\r\n\r\n ngOnInit(): void \r\n {\r\n this.maxHeight = `${window.innerHeight - 250}px`;\r\n }\r\n\r\n\r\n ngAfterViewInit(): void \r\n {\r\n if(this.viewCtrl == null) return;\r\n\r\n if(this.eventBuilder)\r\n {\r\n if(this.viewCtrl == null) return;\r\n \r\n this.eventBuilder.init(this.renderer, this.viewCtrl, this.options);\r\n }\r\n\r\n if(this.selectionManager)\r\n {\r\n this.selectionManager.init(this.viewCtrl);\r\n }\r\n }\r\n\r\n onViewReady(): void\r\n {\r\n this.viewReadyEvent.emit();\r\n }\r\n\r\n}\r\n","\r\n<div [dir]=\"options.direction\">\r\n\r\n <div [classList]=\"titleClassList + ' font-bold text-xl'\" style=\"border-bottom: 1px solid #000; text-align: center;\">\r\n {{title}}\r\n </div>\r\n\r\n <div [style.maxHeight]=\"maxHeight\" style=\"overflow: scroll;min-height: 300px;\">\r\n\r\n <div style=\"flex: 1;display: flex;flex-direction: column;\" class=\"adc-table\" tableViewController (viewReady)=\"onViewReady()\">\r\n\r\n <ng-container *ngFor=\"let row of tableRows; let lastRow = last; let rowIndex = index\">\r\n \r\n <div class=\"row\" style=\"position: relative;flex-shrink: 0;\" [attr.sticky]=\"rowIndex == 0\" \r\n [class.sticky]=\"rowIndex == 0\" [class.vertical-stick]=\"rowIndex == 0\" [attr.row-index]=\"rowIndex\">\r\n \r\n <!-- cells - columns -->\r\n <div style=\"display: flex;flex-direction: row;flex-wrap: nowrap;flex: 1;height: 100%;\" cells>\r\n \r\n <ng-container *ngIf=\"showRow\">\r\n \r\n <div style=\"min-width: 100px;display: flex;flex-direction: column;overflow: hidden;\r\n padding: 6px;\" [style.justifyContent]=\"row.verticalAlign\" [style.alignItems]=\"row.horizontalAlign\"\r\n [classList]=\"row.classList + ' column column-border sticky horizontal-stick'\" detail=\"true\" [style.borderBottom]=\"lastRow ? 'none' : '1px solid #000'\">\r\n \r\n <span class=\"label\">{{row.prefix}}</span>\r\n <span class=\"label\">{{row.label}}</span>\r\n <span class=\"label\">{{row.suffix}}</span>\r\n \r\n </div>\r\n \r\n </ng-container>\r\n \r\n <ng-container *ngFor=\"let column of row.columns; let lastColumn = last; let columnIndex = index\">\r\n <div style=\"min-width: 50px;display: flex;flex-direction: column;overflow: hidden;\r\n white-space: nowrap;text-overflow: ellipsis;padding: 6px;flex: 1;\" [style.justifyContent]=\"column.verticalAlign\" [attr.selectable]=\"column.selectable\"\r\n [classList]=\"column.classList + ' column'\" [attr.columnValue]=\"column.value\" [style.alignItems]=\"column.horizontalAlign\"\r\n [attr.rowValue]=\"row.value\" [attr.rowIndex]=\"rowIndex\" [attr.columnIndex]=\"columnIndex\"\r\n [ngClass]=\"{'column-border': !lastColumn}\" [style.borderBottom]=\"lastRow ? 'none' : '1px solid #000'\" detail=\"false\">\r\n \r\n <span class=\"label\">{{column.prefix}}</span>\r\n <span class=\"label\">{{column.label}}</span>\r\n <span class=\"label\">{{column.suffix}}</span>\r\n \r\n </div>\r\n </ng-container>\r\n \r\n </div>\r\n \r\n <!-- events -->\r\n <div style=\"position: absolute;height: 100%;width: 100%;left: 0;top: 0;z-index: 1;pointer-events: none;\" events></div>\r\n\r\n <div style=\"position: absolute;height: 100%;width: 100%;left: 0;top: 0;z-index: 3;pointer-events: none;\" tooltip></div>\r\n \r\n </div>\r\n \r\n </ng-container>\r\n \r\n </div>\r\n\r\n </div>\r\n\r\n</div>\r\n","import { merge, Observable, Subject } from 'rxjs';\r\n\r\n\r\nexport class DateChangeService {\r\n\r\n private _next: Subject<void> = new Subject<void>();\r\n private _prev: Subject<void> = new Subject<void>();\r\n private _today: Subject<void> = new Subject<void>();\r\n\r\n\r\n constructor() { }\r\n\r\n next(): void\r\n {\r\n this._next.next();\r\n }\r\n\r\n previous(): void\r\n {\r\n this._prev.next();\r\n }\r\n\r\n today(): void\r\n {\r\n this._today.next();\r\n }\r\n\r\n onNext(): Observable<void>\r\n {\r\n return this._next.asObservable();\r\n }\r\n\r\n onPrevious(): Observable<void>\r\n {\r\n return this._prev.asObservable();\r\n }\r\n\r\n onToday(): Observable<void>\r\n {\r\n return this._today.asObservable();\r\n }\r\n\r\n onDateRangeChanged(): Observable<void>\r\n {\r\n return merge(this.onNext(), this.onPrevious(), this.onToday());\r\n }\r\n}\r\n","\r\n\r\nexport class ADCStaticValues {\r\n\r\n constructor() { }\r\n\r\n static getHoursOfDay(): string[] {\r\n const hours: string[] = [\r\n '00:00',\r\n '01:00',\r\n '02:00',\r\n '03:00',\r\n '04:00',\r\n '05:00',\r\n '06:00',\r\n '07:00',\r\n '08:00',\r\n '09:00',\r\n '10:00',\r\n '11:00',\r\n '12:00',\r\n '13:00',\r\n '14:00',\r\n '15:00',\r\n '16:00',\r\n '17:00',\r\n '18:00',\r\n '19:00',\r\n '20:00',\r\n '21:00',\r\n '22:00',\r\n '23:00',\r\n ]\r\n\r\n return hours;\r\n }\r\n\r\n static getDaysOfWeek(): string[] \r\n {\r\n const days: string[] = [\r\n 'Sunday',\r\n 'Monday',\r\n 'Tuesday',\r\n 'Wednesday',\r\n 'Thursday',\r\n 'Friday',\r\n 'Saturday',\r\n ];\r\n return days;\r\n }\r\n}\r\n","\r\nimport { ADCIDateAdapter, ADCILabels } from '../interface';\r\n\r\nexport class ADCCommonService {\r\n\r\n\r\n\r\n constructor(\r\n private dateAdapter: ADCIDateAdapter,\r\n private labels: ADCILabels | undefined\r\n ) { }\r\n\r\n lengthOfDayWeeks = 7;\r\n\r\n getMonthName(name: string): string | undefined\r\n {\r\n return this.labels?.monthsOfYear[name]!;\r\n }\r\n\r\n getDayIndex(index: number): number\r\n {\r\n return (index + this.dateAdapter.startDayOfweek) % this.lengthOfDayWeeks;\r\n }\r\n}\r\n","\r\n/**\r\n * A utility class for handling and parsing date and time strings.\r\n * Provides methods to extract date parts, time parts, hours, and minutes from formatted date strings.\r\n * This class is designed to work with ISO 8601-like datetime formats.\r\n */\r\nexport class ADCDateTimeTools {\r\n\r\n /** \r\n * Allowed date splitters for parsing date strings.\r\n */\r\n private readonly dateSplitters: string[] = ['-', '/'];\r\n \r\n /**\r\n * Constructs a dynamic regex pattern for date splitters.\r\n * \r\n * @returns A string pattern containing allowed splitters.\r\n */\r\n private getDateSplitterPattern(): string {\r\n // Escape special characters for regex and join them into a character class.\r\n return `[${this.dateSplitters.map(char => `\\\\${char}`).join('')}]`;\r\n }\r\n \r\n /**\r\n * Extracts the date portion (YYYY-MM-DD or YYYY/MM/DD) from a datetime string.\r\n * \r\n * @param source - A datetime string in the format `yyyy-MM-ddThh:mm:ss` or `yyyy-MM-dd`.\r\n * @returns The extracted date in the format `yyyy-MM-dd` or `yyyy/MM/dd`.\r\n * @throws If the source string is not a valid date string.\r\n * \r\n * @example\r\n * const tools = new ADCDateTimeTools();\r\n * console.log(tools.dateOnly('2024-11-12T15:30:00')); // Output: '2024-11-12'\r\n */\r\n dateOnly(source: string): string {\r\n const splitterPattern = this.getDateSplitterPattern();\r\n const regex = new RegExp(`^\\\\d{4}${splitterPattern}(0[1-9]|1[0-2])${splitterPattern}(0[1-9]|[12]\\\\d|3[01])`);\r\n \r\n if (!regex.test(source)) {\r\n throw new Error(`Invalid date string format: ${source}. Expected formats: yyyy-MM-dd or yyyy/MM/dd`);\r\n }\r\n \r\n return source.split('T')[0];\r\n }\r\n \r\n /**\r\n * Extracts the time portion (hh:mm:ss) from a datetime string.\r\n * \r\n * @param source - A datetime string in the format `yyyy-MM-ddThh:mm:ss` or `yyyy-MM-ddThh:mm`.\r\n * @returns The extracted time in the format `hh:mm:ss` or `hh:mm`.\r\n * @throws If the source string is not a valid datetime string.\r\n * \r\n * @example\r\n * const tools = new ADCDateTimeTools();\r\n * console.log(tools.timeOnly('2024-11-12T15:30:00')); // Output: '15:30:00'\r\n */\r\n timeOnly(source: string): string {\r\n const regex = /.*T(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$/;\r\n \r\n if (!regex.test(source)) {\r\n throw new Error(`Invalid datetime string format: ${source}. Expected format: *Thh:mm:ss or *Thh:mm`);\r\n }\r\n \r\n return source.split('T')[1];\r\n }\r\n \r\n /**\r\n * Extracts the hour from a time string.\r\n * \r\n * @param source - A time string in the format `hh:mm:ss` or `hh:mm`.\r\n * @returns The hour as a string.\r\n * @throws If the source string is not a valid time string.\r\n * \r\n * @example\r\n * const tools = new ADCDateTimeTools();\r\n * console.log(tools.hour('15:45')); // Output: '15'\r\n */\r\n hour(source: string): string {\r\n const regex = /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$/;\r\n \r\n if (!regex.test(source)) {\r\n throw new Error(`Invalid time string format: ${source}. Expected format: hh:mm:ss or hh:mm`);\r\n }\r\n \r\n return source.split(':')[0];\r\n }\r\n \r\n /**\r\n * Extracts the hour from a time string or returns a default value if the source is null or undefined.\r\n * \r\n * @param source - A time string in the format `hh:mm:ss` or `hh:mm`, or `null`/`undefined`.\r\n * @param defaultHour - The default hour to return if the source is null or invalid.\r\n * @returns The hour as a string or the default value.\r\n * \r\n * @example\r\n * const tools = new ADCDateTimeTools();\r\n * console.log(tools.hourOrDefault(null, '08')); // Output: '08'\r\n */\r\n hourOrDefault(source: string | null | undefined, defaultHour: string): string {\r\n if (source == null) return defaultHour;\r\n \r\n const regex = /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$/;\r\n \r\n if (!regex.test(source)) {\r\n throw new Error(`Invalid time string format: ${source}. Expected format: hh:mm:ss or hh:mm`);\r\n }\r\n \r\n return source.split(':')[0];\r\n }\r\n \r\n /**\r\n * Extracts the minutes from a time string.\r\n * \r\n * @param source - A time string in the format `hh:mm:ss` or `hh:mm`.\r\n * @returns The minutes as a string.\r\n * @throws If the source string is not a valid time string.\r\n * \r\n * @example\r\n * const tools = new ADCDateTimeTools();\r\n * console.log(tools.minutes('15:45')); // Output: '45'\r\n */\r\n minutes(source: string): string {\r\n const regex = /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$/;\r\n \r\n if (!regex.test(source)) {\r\n throw new Error(`Invalid time string format: ${source}. Expected format: hh:mm:ss or hh:mm`);\r\n }\r\n \r\n return source.split(':')[1];\r\n }\r\n\r\n /**\r\n * Extracts the minutes from a time string or returns a default value if the source is null or undefined.\r\n * \r\n * @param source - A time string in the format `hh:mm:ss` or `hh:mm`, or `null`/`undefined`.\r\n * @param defaultMinute - The default minute to return if the source is null or invalid.\r\n * @returns The minutes as a string or the default value.\r\n * \r\n * @example\r\n * const tools = new ADCDateTimeTools();\r\n * console.log(tools.minutesOrDefault(null, '30')); // Output: '30'\r\n */\r\nminutesOrDefault(source: string | null | undefined, defaultMinute: string): string {\r\n if (source == null) return defaultMinute;\r\n\r\n const regex = /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$/;\r\n\r\n if (!regex.test(source)) {\r\n throw new Error(`Invalid time string format: ${source}. Expected format: hh:mm:ss or hh:mm`);\r\n }\r\n\r\n return source.split(':')[1];\r\n}\r\n }","import { debounceTime, distinctUntilChanged, EMPTY, fromEvent, map, merge, Observable, of, startWith, switchMap, takeUntil, tap } from \"rxjs\";\r\n\r\nexport class Interactions\r\n{\r\n static readonly click = (element: HTMLElement, destroy: Observable<void>): Observable<MouseEvent> =>\r\n fromEvent(element, 'click').pipe(takeUntil(destroy), map(e => e as MouseEvent));\r\n\r\n static readonly tooltip = (element: HTMLElement, delay: number, destroy: Observable<void>, scrollableContainer: HTMLElement | null = null): Observable<{state: 'show' | 'hide', coordinates: {x: number, y: number}}> => {\r\n\r\n const mouseEnter$ = fromEvent<MouseEvent>(element, 'mouseenter');\r\n const mouseLeave$ = fromEvent<MouseEvent>(element, 'mouseleave').pipe(map(e => {\r\n return {state: 'hide' as const, coordinates: {x: e.clientX, y: e.clientY}}\r\n }));\r\n const mouseMove$ = fromEvent<MouseEvent>(element, 'mousemove');\r\n\r\n const scrollObservables = [\r\n fromEvent(window, 'scroll'),\r\n ...(scrollableContainer ? [fromEvent(scrollableContainer, 'scroll')] : [])\r\n ];\r\n \r\n const scroll$ = merge(...scrollObservables).pipe(\r\n map(() => ({ state: 'hide' as const, coordinates: { x: 0, y: 0 } }))\r\n );\r\n\r\n return mouseEnter$.pipe(\r\n switchMap(() => \r\n merge(\r\n mouseMove$.pipe(map((e) => {\r\n return {state: 'hide' as const, coordinates: {x: e.clientX, y: e.clientY}}\r\n })), // Hide on every move\r\n mouseMove$.pipe(debounceTime(delay), map((e) => {\r\n return {state: 'show' as const, coordinates: {x: e.clientX, y: e.clientY}}\r\n })), // Show after delay if steady\r\n mouseLeave$,\r\n scroll$\r\n ).pipe(\r\n takeUntil(mouseLeave$),\r\n distinctUntilChanged((prev, curr) => prev.state === curr.state)\r\n )\r\n ),\r\n takeUntil(destroy)\r\n );\r\n }\r\n}\r\n\r\n","import { Renderer2 } from \"@angular/core\";\r\nimport { ADCIOptions, ADCITableEvent, ADCITableEventAttributes, ADCITableEventSelectEvent, ADCITableViewCTRL } from \"./interface\";\r\nimport { ADCTableRowTools } from \"./utils/table-view-row.tools\";\r\nimport { Subject } from \"rxjs\";\r\nimport { Interactions } from \"./Interactions\";\r\n\r\nexport class FlatEventBuilder \r\n{\r\n private _data: ADCITableEvent[] = [];\r\n private _tvc: ADCITableViewCTRL | null = null;\r\n private _options: ADCIOptions | null = null;\r\n private _renderer: Renderer2 | null = null;\r\n\r\n private eventHeight = 40;\r\n private eventSelectSubject = new Subject<ADCITableEventSelectEvent>();\r\n private eventClearSubject = new Subject<void>();\r\n\r\n\r\n readonly eventSelectionStream = this.eventSelectSubject.asObservable();\r\n\r\n set data(data: ADCITableEvent[])\r\n {\r\n data = Array.isArray(data) ? data : [];\r\n this._data = data;\r\n\r\n this.buildEvents();\r\n }\r\n get data(): ADCITableEvent[]\r\n {\r\n return this._data;\r\n }\r\n\r\n init(\r\n renderer: Renderer2,\r\n tvc: ADCITableViewCTRL,\r\n options: ADCIOptions\r\n )\r\n {\r\n this._tvc = tvc;\r\n this._options = options;\r\n this._renderer = renderer;\r\n\r\n this.buildEvents();\r\n }\r\n\r\n clearEvents(): void \r\n {\r\n if(this._tvc == null) return;\r\n \r\n const rowsCount = this._tvc.rowsCount();\r\n\r\n this.eventClearSubject.next();\r\n\r\n for(let i = 0; i < rowsCount; i++)\r\n {\r\n this._tvc.row(i).clearEvents();\r\n }\r\n }\r\n\r\n buildEvents(): void\r\n {\r\n if(this._tvc == null) return;\r\n \r\n this.clearEvents(); \r\n const events = this.data;\r\n const rowsCount = this._tvc.rowsCount();\r\n\r\n for(let i = 0; i < rowsCount; i++)\r\n {\r\n const row = this._tvc.row(i);\r\n\r\n if(row.isSticky) continue;\r\n\r\n const rowEvents = events.filter(e => e.rowStart <= i && e.rowEnd >= i);\r\n \r\n if(rowEvents.length == 0)\r\n {\r\n const height = this.heightUpdateStrategy(0);\r\n row.setHeight(height); \r\n continue;\r\n } \r\n\r\n let rowMaxStack = 0; \r\n\r\n for(const e of rowEvents)\r\n {\r\n const attributes = this.calculateAttributes(e, row, rowMaxStack);\r\n \r\n if(attributes.width == '0%') continue;\r\n\r\n if(attributes.level + 1 > rowMaxStack)\r\n {\r\n rowMaxStack = attributes.level + 1;\r\n const height = this.heightUpdateStrategy(rowMaxStack);\r\n row.setHeight(height);\r\n }\r\n \r\n this.buildEvent(e, attributes, row);\r\n }\r\n }\r\n }\r\n\r\n private heightUpdateStrategy = (eventsCount: number) => {\r\n return `${Math.max(eventsCount * (this.eventHeight + 5) + 50, 70)}px`;\r\n }\r\n\r\n private calculateAttributes(event: ADCITableEvent, row: ADCTableRowTools, maxStackSize: number): ADCITableEventAttributes\r\n {\r\n if(this._options == null || this._tvc == null)\r\n {\r\n throw new Error(\"required options are not provided\");\r\n }\r\n\r\n const direction = this._options.direction;\r\n const horizontalOffset = this.horizontalOffset(event, row);\r\n const width = this.width(event, row);\r\n const level = this.level(event, row, maxStackSize);\r\n \r\n const attributes: ADCITableEventAttributes = \r\n {\r\n backgroundColor: event.data.bgColor,\r\n left: direction === 'rtl' ? 'unset' : `${horizontalOffset}`,\r\n right: direction === 'rtl' ? `${horizontalOffset}` : 'unset',\r\n textAlign: direction === 'rtl' ? 'right' : 'left',\r\n level: level,\r\n width: width,\r\n classList: ['table-event']\r\n }\r\n \r\n if(event.rowStart == row.index && event.columnStart != null)\r\n {\r\n attributes.classList.push('event-start-day');\r\n }\r\n \r\n if(event.rowEnd == row.index && event.columnEnd != null)\r\n {\r\n attributes.classList.push('event-end-day')\r\n }\r\n \r\n return attributes;\r\n }\r\n\r\n private horizontalOffset(event: ADCITableEvent, row: ADCTableRowTools): string\r\n {\r\n const offsetX = event.rowStart != row.index || event.columnStart == null ? 0 : event.offsetX;\r\n const columnStart = event.columnStart == null || row.index != event.rowStart ? 0 : event.columnStart;\r\n\r\n const cellWidth = row.width / row.cells.length;\r\n\r\n return `${cellWidth * (offsetX + columnStart) + row.horizontalOffset}px`;\r\n }\r\n\r\n private width(event: ADCITableEvent, row: ADCTableRowTools): string\r\n {\r\n const offsetX = row.index == event.rowStart && event.columnStart != null ? event.offsetX : 0;\r\n const columnStart = event.columnStart == null || row.index != event.rowStart ? 0 : event.columnStart;\r\n const fractionX = row.index == event.rowEnd && event.columnEnd != null ? event.fractionX : 1;\r\n const columnEnd = event.columnEnd == null || row.index != event.rowEnd ? (row.cells.length - 1) : event.columnEnd;\r\n\r\n const cellWidth = columnEnd - columnStart - offsetX + fractionX;\r\n\r\n return (row.width / row.cells.length) * cellWidth + 'px';\r\n }\r\n\r\n private level(event: ADCITableEvent, row: ADCTableRowTools, maxStackSize: number): number\r\n {\r\n const previousViewEvents = row.attachedEvents;\r\n\r\n const previousEventIds = previousViewEvents.map((viewEvent: HTMLElement) => {\r\n return viewEvent.getAttribute('id');\r\n });\r\n\r\n const previousEvents = this.data.filter(item => previousEventIds.includes(item.data.id.toString()));\r\n \r\n let selfStart = event.columnStart == null ? event.offsetX + event.overlapTolerance : event.columnStart + event.offsetX + event.overlapTolerance;\r\n\r\n if(row.index != event.rowStart)\r\n {\r\n selfStart = 0;\r\n }\r\n\r\n let selfEnd = event.columnEnd == null ? (row.cells.length + event.fractionX - event.overlapTolerance - 1) : event.columnEnd + event.fractionX - event.overlapTolerance;\r\n\r\n if(row.index != event.rowEnd)\r\n {\r\n selfEnd = row.cells.length;\r\n }\r\n\r\n const collisionneurEvents = previousEvents.filter(e => {\r\n\r\n if(e.columnStart == null && e.columnEnd == null)\r\n {\r\n return true;\r\n }\r\n\r\n let eventStart = e.columnStart == null ? e.offsetX : (e.columnStart + e.offsetX);\r\n\r\n if(e.rowStart != row.index)\r\n {\r\n eventStart = 0;\r\n }\r\n\r\n let eventEnd = e.columnEnd == null ? row.cells.length + e.fractionX - 1 : e.columnEnd + e.fractionX;\r\n\r\n if(e.rowEnd != row.index)\r\n {\r\n eventEnd = row.cells.length;\r\n }\r\n\r\n return (selfStart >= eventStart && selfStart <= eventEnd) ||\r\n (selfEnd >= eventStart && selfEnd <= eventEnd) ||\r\n (selfStart < eventStart && selfEnd > eventEnd);\r\n });\r\n \r\n const collisionneurViewEvents = previousViewEvents.filter((viewEvent: HTMLElement) => \r\n collisionneurEvents.map((e) => e.data.id.toString()).includes(viewEvent.getAttribute('id')!)\r\n );\r\n\r\n var level = 0;\r\n\r\n for(let i = 0; i <= maxStackSize; i++)\r\n {\r\n const isLevelFilled = collisionneurViewEvents.map((viewEvent: HTMLElement) => viewEvent.getAttribute('level')).includes(i.toString());\r\n\r\n if(!isLevelFilled)\r\n {\r\n level = i;\r\n break;\r\n }\r\n }\r\n\r\n return level;\r\n }\r\n\r\n private buildEvent(event: ADCITableEvent, attributes: ADCITableEventAttributes, row: ADCTableRowTools): void\r\n {\r\n if(this._renderer == null)\r\n {\r\n throw new Error(\"options are not provided\");\r\n }\r\n\r\n const eventElRef: HTMLElement = this._renderer.createElement('div');\r\n\r\n this._renderer.appendChild(eventElRef, this._renderer.createText(event.data.title));\r\n\r\n this._renderer.setStyle(eventElRef, 'background-color', attributes.backgroundColor);\r\n this._renderer.setStyle(eventElRef, 'left', attributes.left);\r\n this._renderer.setStyle(eventElRef, 'right', attributes.right);\r\n this._renderer.setStyle(eventElRef, 'top', `${attributes.level * (this.eventHeight + 5) + 25}px`);\r\n this._renderer.setStyle(eventElRef, 'width', `${attributes.width}`);\r\n this._renderer.setStyle(eventElRef, 'height', `${this.eventHeight}px`);\r\n this._renderer.setStyle(eventElRef, 'text-align', attributes.textAlign);\r\n\r\n this._renderer.setAttribute(eventElRef, 'id', event.data.id.toString());\r\n this._renderer.setAttribute(eventElRef, 'level', attributes.level.toString());\r\n this._renderer.setAttribute(eventElRef, 'row-index', row.index.toString());\r\n\r\n attributes.classList.forEach((className: string) => {\r\n this._renderer!.addClass(eventElRef, className);\r\n });\r\n\r\n if(event.data.isClickable)\r\n {\r\n Interactions.click(eventElRef, this.eventClearSubject.asObservable()).subscribe((e) => {\r\n this.eventSelectSubject.next({dom: eventElRef, event: event, jsEvent: e});\r\n });\r\n\r\n this._renderer.setStyle(eventElRef, 'cursor', 'pointer');\r\n }\r\n\r\n\r\n if(event.data.tooltip)\r\n {\r\n const tooltipRef = this._renderer.createElement('div');\r\n\r\n this._renderer.addClass(tooltipRef, 'tooltip');\r\n\r\n this._renderer.setAttribute(tooltipRef, 'id', event.data.id.toString());\r\n this._renderer.appendChild(tooltipRef, this._renderer.createText(event.data.tooltip));\r\n\r\n Interactions.tooltip(eventElRef, 300, this.eventClearSubject.asObservable(), this._tvc?.scrollableContainer).subscribe(e => {\r\n\r\n if(e.state === 'hide')\r\n {\r\n row.clearTooltips();\r\n }\r\n else\r\n {\r\n row.attachTooltip(tooltipRef, e.coordinates);\r\n }\r\n\r\n });\r\n }\r\n \r\n\r\n row.attachEvent(eventElRef);\r\n }\r\n}","import { Subject } from \"rxjs\";\r\nimport { ADCITableCell, ADCITableViewCTRL } from \"./interface\";\r\nimport { ADCTableCell } from