@almaobservatory/ngx-datatable
Version:
ngx-datatable is an Angular table grid component for presenting large and complex data.
1 lines • 308 kB
Source Map (JSON)
{"version":3,"file":"almaobservatory-ngx-datatable.mjs","sources":["../../../../projects/swimlane/ngx-datatable/src/lib/services/scrollbar-helper.service.ts","../../../../projects/swimlane/ngx-datatable/src/lib/services/dimensions-helper.service.ts","../../../../projects/swimlane/ngx-datatable/src/lib/services/column-changes.service.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/footer/footer-template.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/directives/visibility.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/directives/draggable.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/directives/resizeable.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/directives/orderable.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/directives/long-press.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/scroller.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body-group-header-template.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body-group-header.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/column-prop-getters.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/tree.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/camel-case.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/id.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/column-helper.ts","../../../../projects/swimlane/ngx-datatable/src/lib/types/column-mode.type.ts","../../../../projects/swimlane/ngx-datatable/src/lib/types/selection.type.ts","../../../../projects/swimlane/ngx-datatable/src/lib/types/sort.type.ts","../../../../projects/swimlane/ngx-datatable/src/lib/types/contextmenu.type.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/columns/column-header.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/columns/column-cell.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/columns/tree.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/columns/column.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/row-detail/row-detail-template.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/row-detail/row-detail.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/footer/footer.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/column.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/row-height-cache.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/prefixes.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/translate.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/progress-bar.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/selection.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/keys.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/selection.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/types/sort-direction.type.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body-cell.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body-row.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/summary/summary-row.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body-row-wrapper.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/sort.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/header/header-cell.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/header/header.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/throttle.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/math.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/footer/pager.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/footer/footer.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/datatable.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/datatable.component.html","../../../../projects/swimlane/ngx-datatable/src/lib/ngx-datatable.module.ts","../../../../projects/swimlane/ngx-datatable/src/lib/types/click.type.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/elm-from-point.ts","../../../../projects/swimlane/ngx-datatable/src/public-api.ts","../../../../projects/swimlane/ngx-datatable/src/almaobservatory-ngx-datatable.ts"],"sourcesContent":["import { Inject, Injectable } from '@angular/core';\r\nimport { DOCUMENT } from '@angular/common';\r\n\r\n/**\r\n * Gets the width of the scrollbar. Nesc for windows\r\n * http://stackoverflow.com/a/13382873/888165\r\n */\r\n@Injectable()\r\nexport class ScrollbarHelper {\r\n width: number = this.getWidth();\r\n\r\n constructor(@Inject(DOCUMENT) private document: any) {}\r\n\r\n getWidth(): number {\r\n const outer = this.document.createElement('div');\r\n outer.style.visibility = 'hidden';\r\n outer.style.width = '100px';\r\n outer.style.msOverflowStyle = 'scrollbar';\r\n this.document.body.appendChild(outer);\r\n\r\n const widthNoScroll = outer.offsetWidth;\r\n outer.style.overflow = 'scroll';\r\n\r\n const inner = this.document.createElement('div');\r\n inner.style.width = '100%';\r\n outer.appendChild(inner);\r\n\r\n const widthWithScroll = inner.offsetWidth;\r\n outer.parentNode.removeChild(outer);\r\n\r\n return widthNoScroll - widthWithScroll;\r\n }\r\n}\r\n","import { Inject, Injectable } from '@angular/core';\r\n\r\n/**\r\n * Gets the width of the scrollbar. Nesc for windows\r\n * http://stackoverflow.com/a/13382873/888165\r\n */\r\n@Injectable()\r\nexport class DimensionsHelper {\r\n getDimensions(element: Element): ClientRect {\r\n return element.getBoundingClientRect();\r\n }\r\n}\r\n","import { Injectable } from '@angular/core';\r\n\r\nimport { Observable, Subject } from 'rxjs';\r\n\r\n/**\r\n * service to make DatatableComponent aware of changes to\r\n * input bindings of DataTableColumnDirective\r\n */\r\n@Injectable()\r\nexport class ColumnChangesService {\r\n private columnInputChanges = new Subject<void>();\r\n\r\n get columnInputChanges$(): Observable<void> {\r\n return this.columnInputChanges.asObservable();\r\n }\r\n\r\n onInputChange(): void {\r\n this.columnInputChanges.next();\r\n }\r\n}\r\n","import { Directive, TemplateRef } from '@angular/core';\r\n\r\n@Directive({ selector: '[ngx-datatable-footer-template]' })\r\nexport class DataTableFooterTemplateDirective {\r\n constructor(public template: TemplateRef<any>) {}\r\n}\r\n","import { Directive, Output, EventEmitter, ElementRef, HostBinding, NgZone, OnInit, OnDestroy } from '@angular/core';\r\n\r\n/**\r\n * Visibility Observer Directive\r\n *\r\n * Usage:\r\n *\r\n * \t\t<div\r\n * \t\t\tvisibilityObserver\r\n * \t\t\t(visible)=\"onVisible($event)\">\r\n * \t\t</div>\r\n *\r\n */\r\n@Directive({ selector: '[visibilityObserver]' })\r\nexport class VisibilityDirective implements OnInit, OnDestroy {\r\n @HostBinding('class.visible')\r\n isVisible: boolean = false;\r\n\r\n @Output() visible: EventEmitter<any> = new EventEmitter();\r\n\r\n timeout: any;\r\n\r\n constructor(private element: ElementRef, private zone: NgZone) {}\r\n\r\n ngOnInit(): void {\r\n this.runCheck();\r\n }\r\n\r\n ngOnDestroy(): void {\r\n clearTimeout(this.timeout);\r\n }\r\n\r\n onVisibilityChange(): void {\r\n // trigger zone recalc for columns\r\n this.zone.run(() => {\r\n this.isVisible = true;\r\n this.visible.emit(true);\r\n });\r\n }\r\n\r\n runCheck(): void {\r\n const check = () => {\r\n // https://davidwalsh.name/offsetheight-visibility\r\n const { offsetHeight, offsetWidth } = this.element.nativeElement;\r\n\r\n if (offsetHeight && offsetWidth) {\r\n clearTimeout(this.timeout);\r\n this.onVisibilityChange();\r\n } else {\r\n clearTimeout(this.timeout);\r\n this.zone.runOutsideAngular(() => {\r\n this.timeout = setTimeout(() => check(), 50);\r\n });\r\n }\r\n };\r\n\r\n this.timeout = setTimeout(() => check());\r\n }\r\n}\r\n","import { Directive, ElementRef, Input, Output, EventEmitter, OnDestroy, OnChanges, SimpleChanges } from '@angular/core';\r\nimport { Subscription, fromEvent } from 'rxjs';\r\nimport { takeUntil } from 'rxjs/operators';\r\n\r\n/**\r\n * Draggable Directive for Angular2\r\n *\r\n * Inspiration:\r\n * https://github.com/AngularClass/angular2-examples/blob/master/rx-draggable/directives/draggable.ts\r\n * http://stackoverflow.com/questions/35662530/how-to-implement-drag-and-drop-in-angular2\r\n *\r\n */\r\n@Directive({ selector: '[draggable]' })\r\nexport class DraggableDirective implements OnDestroy, OnChanges {\r\n @Input() dragEventTarget: any;\r\n @Input() dragModel: any;\r\n @Input() dragX: boolean = true;\r\n @Input() dragY: boolean = true;\r\n\r\n @Output() dragStart: EventEmitter<any> = new EventEmitter();\r\n @Output() dragging: EventEmitter<any> = new EventEmitter();\r\n @Output() dragEnd: EventEmitter<any> = new EventEmitter();\r\n\r\n element: HTMLElement;\r\n isDragging: boolean = false;\r\n subscription: Subscription;\r\n\r\n constructor(element: ElementRef) {\r\n this.element = element.nativeElement;\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges): void {\r\n if (changes['dragEventTarget'] && changes['dragEventTarget'].currentValue && this.dragModel.dragging) {\r\n this.onMousedown(changes['dragEventTarget'].currentValue);\r\n }\r\n }\r\n\r\n ngOnDestroy(): void {\r\n this._destroySubscription();\r\n }\r\n\r\n onMouseup(event: MouseEvent): void {\r\n if (!this.isDragging) return;\r\n\r\n this.isDragging = false;\r\n this.element.classList.remove('dragging');\r\n\r\n if (this.subscription) {\r\n this._destroySubscription();\r\n this.dragEnd.emit({\r\n event,\r\n element: this.element,\r\n model: this.dragModel\r\n });\r\n }\r\n }\r\n\r\n onMousedown(event: MouseEvent): void {\r\n // we only want to drag the inner header text\r\n const isDragElm = (<HTMLElement>event.target).classList.contains('draggable');\r\n\r\n if (isDragElm && (this.dragX || this.dragY)) {\r\n event.preventDefault();\r\n this.isDragging = true;\r\n\r\n const mouseDownPos = { x: event.clientX, y: event.clientY };\r\n\r\n const mouseup = fromEvent(document, 'mouseup');\r\n this.subscription = mouseup.subscribe((ev: MouseEvent) => this.onMouseup(ev));\r\n\r\n const mouseMoveSub = fromEvent(document, 'mousemove')\r\n .pipe(takeUntil(mouseup))\r\n .subscribe((ev: MouseEvent) => this.move(ev, mouseDownPos));\r\n\r\n this.subscription.add(mouseMoveSub);\r\n\r\n this.dragStart.emit({\r\n event,\r\n element: this.element,\r\n model: this.dragModel\r\n });\r\n }\r\n }\r\n\r\n move(event: MouseEvent, mouseDownPos: { x: number; y: number }): void {\r\n if (!this.isDragging) return;\r\n\r\n const x = event.clientX - mouseDownPos.x;\r\n const y = event.clientY - mouseDownPos.y;\r\n\r\n if (this.dragX) this.element.style.left = `${x}px`;\r\n if (this.dragY) this.element.style.top = `${y}px`;\r\n\r\n this.element.classList.add('dragging');\r\n\r\n this.dragging.emit({\r\n event,\r\n element: this.element,\r\n model: this.dragModel\r\n });\r\n }\r\n\r\n private _destroySubscription(): void {\r\n if (this.subscription) {\r\n this.subscription.unsubscribe();\r\n this.subscription = undefined;\r\n }\r\n }\r\n}\r\n","import {\r\n Directive,\r\n ElementRef,\r\n HostListener,\r\n Input,\r\n Output,\r\n EventEmitter,\r\n OnDestroy,\r\n AfterViewInit,\r\n Renderer2\r\n} from '@angular/core';\r\nimport { Subscription, fromEvent } from 'rxjs';\r\nimport { takeUntil } from 'rxjs/operators';\r\n\r\n@Directive({\r\n selector: '[resizeable]',\r\n host: {\r\n '[class.resizeable]': 'resizeEnabled'\r\n }\r\n})\r\nexport class ResizeableDirective implements OnDestroy, AfterViewInit {\r\n @Input() resizeEnabled: boolean = true;\r\n @Input() minWidth: number;\r\n @Input() maxWidth: number;\r\n\r\n @Output() resize: EventEmitter<any> = new EventEmitter();\r\n\r\n element: HTMLElement;\r\n subscription: Subscription;\r\n resizing: boolean = false;\r\n private resizeHandle: HTMLElement;\r\n\r\n constructor(element: ElementRef, private renderer: Renderer2) {\r\n this.element = element.nativeElement;\r\n }\r\n\r\n ngAfterViewInit(): void {\r\n const renderer2 = this.renderer;\r\n this.resizeHandle = renderer2.createElement('span');\r\n if (this.resizeEnabled) {\r\n renderer2.addClass(this.resizeHandle, 'resize-handle');\r\n } else {\r\n renderer2.addClass(this.resizeHandle, 'resize-handle--not-resizable');\r\n }\r\n renderer2.appendChild(this.element, this.resizeHandle);\r\n }\r\n\r\n ngOnDestroy(): void {\r\n this._destroySubscription();\r\n if (this.renderer.destroyNode) {\r\n this.renderer.destroyNode(this.resizeHandle);\r\n } else if (this.resizeHandle) {\r\n this.renderer.removeChild(this.renderer.parentNode(this.resizeHandle), this.resizeHandle);\r\n }\r\n }\r\n\r\n onMouseup(): void {\r\n this.resizing = false;\r\n\r\n if (this.subscription && !this.subscription.closed) {\r\n this._destroySubscription();\r\n this.resize.emit(this.element.clientWidth);\r\n }\r\n }\r\n\r\n @HostListener('mousedown', ['$event'])\r\n onMousedown(event: MouseEvent): void {\r\n const isHandle = (<HTMLElement>event.target).classList.contains('resize-handle');\r\n const initialWidth = this.element.clientWidth;\r\n const mouseDownScreenX = event.screenX;\r\n\r\n if (isHandle) {\r\n event.stopPropagation();\r\n this.resizing = true;\r\n\r\n const mouseup = fromEvent(document, 'mouseup');\r\n this.subscription = mouseup.subscribe((ev: MouseEvent) => this.onMouseup());\r\n\r\n const mouseMoveSub = fromEvent(document, 'mousemove')\r\n .pipe(takeUntil(mouseup))\r\n .subscribe((e: MouseEvent) => this.move(e, initialWidth, mouseDownScreenX));\r\n\r\n this.subscription.add(mouseMoveSub);\r\n }\r\n }\r\n\r\n move(event: MouseEvent, initialWidth: number, mouseDownScreenX: number): void {\r\n const movementX = event.screenX - mouseDownScreenX;\r\n const newWidth = initialWidth + movementX;\r\n\r\n const overMinWidth = !this.minWidth || newWidth >= this.minWidth;\r\n const underMaxWidth = !this.maxWidth || newWidth <= this.maxWidth;\r\n\r\n if (overMinWidth && underMaxWidth) {\r\n this.element.style.width = `${newWidth}px`;\r\n }\r\n }\r\n\r\n private _destroySubscription() {\r\n if (this.subscription) {\r\n this.subscription.unsubscribe();\r\n this.subscription = undefined;\r\n }\r\n }\r\n}\r\n","import {\r\n Directive,\r\n Output,\r\n EventEmitter,\r\n ContentChildren,\r\n QueryList,\r\n KeyValueDiffers,\r\n AfterContentInit,\r\n OnDestroy,\r\n Inject\r\n} from '@angular/core';\r\nimport { DraggableDirective } from './draggable.directive';\r\nimport { DOCUMENT } from '@angular/common';\r\n\r\n@Directive({ selector: '[orderable]' })\r\nexport class OrderableDirective implements AfterContentInit, OnDestroy {\r\n @Output() reorder: EventEmitter<any> = new EventEmitter();\r\n @Output() targetChanged: EventEmitter<any> = new EventEmitter();\r\n\r\n @ContentChildren(DraggableDirective, { descendants: true })\r\n draggables: QueryList<DraggableDirective>;\r\n\r\n positions: any;\r\n differ: any;\r\n lastDraggingIndex: number;\r\n\r\n constructor(differs: KeyValueDiffers, @Inject(DOCUMENT) private document: any) {\r\n this.differ = differs.find({}).create();\r\n }\r\n\r\n ngAfterContentInit(): void {\r\n // HACK: Investigate Better Way\r\n this.updateSubscriptions();\r\n this.draggables.changes.subscribe(this.updateSubscriptions.bind(this));\r\n }\r\n\r\n ngOnDestroy(): void {\r\n this.draggables.forEach(d => {\r\n d.dragStart.unsubscribe();\r\n d.dragging.unsubscribe();\r\n d.dragEnd.unsubscribe();\r\n });\r\n }\r\n\r\n updateSubscriptions(): void {\r\n const diffs = this.differ.diff(this.createMapDiffs());\r\n\r\n if (diffs) {\r\n const subscribe = ({ currentValue, previousValue }: any) => {\r\n unsubscribe({ previousValue });\r\n\r\n if (currentValue) {\r\n currentValue.dragStart.subscribe(this.onDragStart.bind(this));\r\n currentValue.dragging.subscribe(this.onDragging.bind(this));\r\n currentValue.dragEnd.subscribe(this.onDragEnd.bind(this));\r\n }\r\n };\r\n\r\n const unsubscribe = ({ previousValue }: any) => {\r\n if (previousValue) {\r\n previousValue.dragStart.unsubscribe();\r\n previousValue.dragging.unsubscribe();\r\n previousValue.dragEnd.unsubscribe();\r\n }\r\n };\r\n\r\n diffs.forEachAddedItem(subscribe);\r\n // diffs.forEachChangedItem(subscribe.bind(this));\r\n diffs.forEachRemovedItem(unsubscribe);\r\n }\r\n }\r\n\r\n onDragStart(): void {\r\n this.positions = {};\r\n\r\n let i = 0;\r\n for (const dragger of this.draggables.toArray()) {\r\n const elm = dragger.element;\r\n const left = parseInt(elm.offsetLeft.toString(), 0);\r\n this.positions[dragger.dragModel.prop] = {\r\n left,\r\n right: left + parseInt(elm.offsetWidth.toString(), 0),\r\n index: i++,\r\n element: elm\r\n };\r\n }\r\n }\r\n\r\n onDragging({ element, model, event }: any): void {\r\n const prevPos = this.positions[model.prop];\r\n const target = this.isTarget(model, event);\r\n\r\n if (target) {\r\n if (this.lastDraggingIndex !== target.i) {\r\n this.targetChanged.emit({\r\n prevIndex: this.lastDraggingIndex,\r\n newIndex: target.i,\r\n initialIndex: prevPos.index\r\n });\r\n this.lastDraggingIndex = target.i;\r\n }\r\n } else if (this.lastDraggingIndex !== prevPos.index) {\r\n this.targetChanged.emit({\r\n prevIndex: this.lastDraggingIndex,\r\n initialIndex: prevPos.index\r\n });\r\n this.lastDraggingIndex = prevPos.index;\r\n }\r\n }\r\n\r\n onDragEnd({ element, model, event }: any): void {\r\n const prevPos = this.positions[model.prop];\r\n\r\n const target = this.isTarget(model, event);\r\n if (target) {\r\n this.reorder.emit({\r\n prevIndex: prevPos.index,\r\n newIndex: target.i,\r\n model\r\n });\r\n }\r\n\r\n this.lastDraggingIndex = undefined;\r\n element.style.left = 'auto';\r\n }\r\n\r\n isTarget(model: any, event: any): any {\r\n let i = 0;\r\n const x = event.x || event.clientX;\r\n const y = event.y || event.clientY;\r\n const targets = this.document.elementsFromPoint(x, y);\r\n\r\n for (const prop in this.positions) {\r\n // current column position which throws event.\r\n const pos = this.positions[prop];\r\n\r\n // since we drag the inner span, we need to find it in the elements at the cursor\r\n if (model.prop !== prop && targets.find((el: any) => el === pos.element)) {\r\n return {\r\n pos,\r\n i\r\n };\r\n }\r\n\r\n i++;\r\n }\r\n }\r\n\r\n private createMapDiffs(): { [key: string]: DraggableDirective } {\r\n return this.draggables.toArray().reduce((acc, curr) => {\r\n acc[curr.dragModel.$$id] = curr;\r\n return acc;\r\n }, {});\r\n }\r\n}\r\n","import { Directive, Input, Output, EventEmitter, HostBinding, HostListener, OnDestroy } from '@angular/core';\r\nimport { Observable, Subscription, fromEvent } from 'rxjs';\r\nimport { takeUntil } from 'rxjs/operators';\r\nimport { MouseEvent } from '../events';\r\n\r\n@Directive({ selector: '[long-press]' })\r\nexport class LongPressDirective implements OnDestroy {\r\n @Input() pressEnabled: boolean = true;\r\n @Input() pressModel: any;\r\n @Input() duration: number = 500;\r\n\r\n @Output() longPressStart: EventEmitter<any> = new EventEmitter();\r\n @Output() longPressing: EventEmitter<any> = new EventEmitter();\r\n @Output() longPressEnd: EventEmitter<any> = new EventEmitter();\r\n\r\n pressing: boolean;\r\n isLongPressing: boolean;\r\n timeout: any;\r\n mouseX: number = 0;\r\n mouseY: number = 0;\r\n\r\n subscription: Subscription;\r\n\r\n @HostBinding('class.press')\r\n get press(): boolean {\r\n return this.pressing;\r\n }\r\n\r\n @HostBinding('class.longpress')\r\n get isLongPress(): boolean {\r\n return this.isLongPressing;\r\n }\r\n\r\n @HostListener('mousedown', ['$event'])\r\n onMouseDown(event: MouseEvent): void {\r\n // don't do right/middle clicks\r\n if (event.which !== 1 || !this.pressEnabled) return;\r\n\r\n // don't start drag if its on resize handle\r\n const target = <HTMLElement>event.target;\r\n if (target.classList.contains('resize-handle')) return;\r\n\r\n this.mouseX = event.clientX;\r\n this.mouseY = event.clientY;\r\n\r\n this.pressing = true;\r\n this.isLongPressing = false;\r\n\r\n const mouseup = fromEvent(document, 'mouseup');\r\n this.subscription = mouseup.subscribe((ev: MouseEvent) => this.onMouseup());\r\n\r\n this.timeout = setTimeout(() => {\r\n this.isLongPressing = true;\r\n this.longPressStart.emit({\r\n event,\r\n model: this.pressModel\r\n });\r\n\r\n this.subscription.add(\r\n fromEvent(document, 'mousemove')\r\n .pipe(takeUntil(mouseup))\r\n .subscribe((mouseEvent: MouseEvent) => this.onMouseMove(mouseEvent))\r\n );\r\n\r\n this.loop(event);\r\n }, this.duration);\r\n\r\n this.loop(event);\r\n }\r\n\r\n onMouseMove(event: MouseEvent): void {\r\n if (this.pressing && !this.isLongPressing) {\r\n const xThres = Math.abs(event.clientX - this.mouseX) > 10;\r\n const yThres = Math.abs(event.clientY - this.mouseY) > 10;\r\n\r\n if (xThres || yThres) {\r\n this.endPress();\r\n }\r\n }\r\n }\r\n\r\n loop(event: MouseEvent): void {\r\n if (this.isLongPressing) {\r\n this.timeout = setTimeout(() => {\r\n this.longPressing.emit({\r\n event,\r\n model: this.pressModel\r\n });\r\n this.loop(event);\r\n }, 50);\r\n }\r\n }\r\n\r\n endPress(): void {\r\n clearTimeout(this.timeout);\r\n this.isLongPressing = false;\r\n this.pressing = false;\r\n this._destroySubscription();\r\n\r\n this.longPressEnd.emit({\r\n model: this.pressModel\r\n });\r\n }\r\n\r\n onMouseup(): void {\r\n this.endPress();\r\n }\r\n\r\n ngOnDestroy(): void {\r\n this._destroySubscription();\r\n }\r\n\r\n private _destroySubscription(): void {\r\n if (this.subscription) {\r\n this.subscription.unsubscribe();\r\n this.subscription = undefined;\r\n }\r\n }\r\n}\r\n","import {\r\n Component,\r\n Input,\r\n ElementRef,\r\n Output,\r\n EventEmitter,\r\n Renderer2,\r\n NgZone,\r\n OnInit,\r\n OnDestroy,\r\n HostBinding,\r\n ChangeDetectionStrategy\r\n} from '@angular/core';\r\n\r\nimport { MouseEvent } from '../../events';\r\n\r\n@Component({\r\n selector: 'datatable-scroller',\r\n template: ` <ng-content></ng-content> `,\r\n host: {\r\n class: 'datatable-scroll'\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush\r\n})\r\nexport class ScrollerComponent implements OnInit, OnDestroy {\r\n @Input() scrollbarV: boolean = false;\r\n @Input() scrollbarH: boolean = false;\r\n\r\n @HostBinding('style.height.px')\r\n @Input()\r\n scrollHeight: number;\r\n\r\n @HostBinding('style.width.px')\r\n @Input()\r\n scrollWidth: number;\r\n\r\n @Output() scroll: EventEmitter<any> = new EventEmitter();\r\n\r\n scrollYPos: number = 0;\r\n scrollXPos: number = 0;\r\n prevScrollYPos: number = 0;\r\n prevScrollXPos: number = 0;\r\n element: any;\r\n parentElement: any;\r\n onScrollListener: any;\r\n\r\n private _scrollEventListener: any = null;\r\n\r\n constructor(private ngZone: NgZone, element: ElementRef, private renderer: Renderer2) {\r\n this.element = element.nativeElement;\r\n }\r\n\r\n ngOnInit(): void {\r\n // manual bind so we don't always listen\r\n if (this.scrollbarV || this.scrollbarH) {\r\n const renderer = this.renderer;\r\n this.parentElement = renderer.parentNode(renderer.parentNode(this.element));\r\n this._scrollEventListener = this.onScrolled.bind(this);\r\n this.parentElement.addEventListener('scroll', this._scrollEventListener);\r\n }\r\n }\r\n\r\n ngOnDestroy(): void {\r\n if (this._scrollEventListener) {\r\n this.parentElement.removeEventListener('scroll', this._scrollEventListener);\r\n this._scrollEventListener = null;\r\n }\r\n }\r\n\r\n setOffset(offsetY: number): void {\r\n if (this.parentElement) {\r\n this.parentElement.scrollTop = offsetY;\r\n }\r\n }\r\n\r\n onScrolled(event: MouseEvent): void {\r\n const dom: Element = <Element>event.currentTarget;\r\n requestAnimationFrame(() => {\r\n this.scrollYPos = dom.scrollTop;\r\n this.scrollXPos = dom.scrollLeft;\r\n this.updateOffset();\r\n });\r\n }\r\n\r\n updateOffset(): void {\r\n let direction: string;\r\n if (this.scrollYPos < this.prevScrollYPos) {\r\n direction = 'down';\r\n } else if (this.scrollYPos > this.prevScrollYPos) {\r\n direction = 'up';\r\n }\r\n\r\n this.scroll.emit({\r\n direction,\r\n scrollYPos: this.scrollYPos,\r\n scrollXPos: this.scrollXPos\r\n });\r\n\r\n this.prevScrollYPos = this.scrollYPos;\r\n this.prevScrollXPos = this.scrollXPos;\r\n }\r\n}\r\n","import { Directive, TemplateRef } from '@angular/core';\r\n\r\n@Directive({\r\n selector: '[ngx-datatable-group-header-template]'\r\n})\r\nexport class DatatableGroupHeaderTemplateDirective {\r\n constructor(public template: TemplateRef<any>) {}\r\n}\r\n","import { Input, Output, EventEmitter, Directive, TemplateRef, ContentChild } from '@angular/core';\r\nimport { DatatableGroupHeaderTemplateDirective } from './body-group-header-template.directive';\r\n\r\n@Directive({ selector: 'ngx-datatable-group-header' })\r\nexport class DatatableGroupHeaderDirective {\r\n /**\r\n * Row height is required when virtual scroll is enabled.\r\n */\r\n @Input() rowHeight: number | ((group?: any, index?: number) => number) = 0;\r\n\r\n @Input('template')\r\n _templateInput: TemplateRef<any>;\r\n\r\n @ContentChild(DatatableGroupHeaderTemplateDirective, { read: TemplateRef, static: true })\r\n _templateQuery: TemplateRef<any>;\r\n\r\n get template(): TemplateRef<any> {\r\n return this._templateInput || this._templateQuery;\r\n }\r\n\r\n /**\r\n * Track toggling of group visibility\r\n */\r\n @Output() toggle: EventEmitter<any> = new EventEmitter();\r\n\r\n /**\r\n * Toggle the expansion of a group\r\n */\r\n toggleExpandGroup(group: any): void {\r\n this.toggle.emit({\r\n type: 'group',\r\n value: group\r\n });\r\n }\r\n\r\n /**\r\n * Expand all groups\r\n */\r\n expandAllGroups(): void {\r\n this.toggle.emit({\r\n type: 'all',\r\n value: true\r\n });\r\n }\r\n\r\n /**\r\n * Collapse all groups\r\n */\r\n collapseAllGroups(): void {\r\n this.toggle.emit({\r\n type: 'all',\r\n value: false\r\n });\r\n }\r\n}\r\n","import { TableColumnProp } from '../types/table-column.type';\r\n\r\n// maybe rename this file to prop-getters.ts\r\n\r\nexport type ValueGetter = (obj: any, prop: TableColumnProp) => any;\r\n\r\n/**\r\n * Always returns the empty string ''\r\n */\r\nexport function emptyStringGetter(): string {\r\n return '';\r\n}\r\n\r\n/**\r\n * Returns the appropriate getter function for this kind of prop.\r\n * If prop == null, returns the emptyStringGetter.\r\n */\r\nexport function getterForProp(prop: TableColumnProp): ValueGetter {\r\n if (prop == null) {\r\n return emptyStringGetter;\r\n }\r\n\r\n if (typeof prop === 'number') {\r\n return numericIndexGetter;\r\n } else {\r\n // deep or simple\r\n if (prop.indexOf('.') !== -1) {\r\n return deepValueGetter;\r\n } else {\r\n return shallowValueGetter;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Returns the value at this numeric index.\r\n * @param row array of values\r\n * @param index numeric index\r\n * @returns any or '' if invalid index\r\n */\r\nexport function numericIndexGetter(row: any[], index: number): any {\r\n if (row == null) {\r\n return '';\r\n }\r\n // mimic behavior of deepValueGetter\r\n if (!row || index == null) {\r\n return row;\r\n }\r\n\r\n const value = row[index];\r\n if (value == null) {\r\n return '';\r\n }\r\n return value;\r\n}\r\n\r\n/**\r\n * Returns the value of a field.\r\n * (more efficient than deepValueGetter)\r\n * @param obj object containing the field\r\n * @param fieldName field name string\r\n */\r\nexport function shallowValueGetter(obj: any, fieldName: string): any {\r\n if (obj == null) {\r\n return '';\r\n }\r\n if (!obj || !fieldName) {\r\n return obj;\r\n }\r\n\r\n const value = obj[fieldName];\r\n if (value == null) {\r\n return '';\r\n }\r\n return value;\r\n}\r\n\r\n/**\r\n * Returns a deep object given a string. zoo['animal.type']\r\n */\r\nexport function deepValueGetter(obj: any, path: string): any {\r\n if (obj == null) {\r\n return '';\r\n }\r\n if (!obj || !path) {\r\n return obj;\r\n }\r\n\r\n // check if path matches a root-level field\r\n // { \"a.b.c\": 123 }\r\n let current = obj[path];\r\n if (current !== undefined) {\r\n return current;\r\n }\r\n\r\n current = obj;\r\n const split = path.split('.');\r\n\r\n if (split.length) {\r\n for (let i = 0; i < split.length; i++) {\r\n current = current[split[i]];\r\n\r\n // if found undefined, return empty string\r\n if (current === undefined || current === null) {\r\n return '';\r\n }\r\n }\r\n }\r\n\r\n return current;\r\n}\r\n","import { getterForProp } from './column-prop-getters';\r\nimport { TableColumnProp } from '../types/table-column.type';\r\n\r\nexport type OptionalValueGetter = (row: any) => any | undefined;\r\nexport function optionalGetterForProp(prop: TableColumnProp): OptionalValueGetter {\r\n return prop && (row => getterForProp(prop)(row, prop));\r\n}\r\n\r\n/**\r\n * This functions rearrange items by their parents\r\n * Also sets the level value to each of the items\r\n *\r\n * Note: Expecting each item has a property called parentId\r\n * Note: This algorithm will fail if a list has two or more items with same ID\r\n * NOTE: This algorithm will fail if there is a deadlock of relationship\r\n *\r\n * For example,\r\n *\r\n * Input\r\n *\r\n * id -> parent\r\n * 1 -> 0\r\n * 2 -> 0\r\n * 3 -> 1\r\n * 4 -> 1\r\n * 5 -> 2\r\n * 7 -> 8\r\n * 6 -> 3\r\n *\r\n *\r\n * Output\r\n * id -> level\r\n * 1 -> 0\r\n * --3 -> 1\r\n * ----6 -> 2\r\n * --4 -> 1\r\n * 2 -> 0\r\n * --5 -> 1\r\n * 7 -> 8\r\n *\r\n *\r\n * @param rows\r\n *\r\n */\r\nexport function groupRowsByParents(rows: any[], from?: OptionalValueGetter, to?: OptionalValueGetter): any[] {\r\n if (from && to) {\r\n const nodeById = {};\r\n const l = rows.length;\r\n let node: TreeNode | null = null;\r\n\r\n nodeById[0] = new TreeNode(); // that's the root node\r\n\r\n const uniqIDs = rows.reduce((arr, item) => {\r\n const toValue = to(item);\r\n if (arr.indexOf(toValue) === -1) {\r\n arr.push(toValue);\r\n }\r\n return arr;\r\n }, []);\r\n\r\n for (let i = 0; i < l; i++) {\r\n // make TreeNode objects for each item\r\n nodeById[to(rows[i])] = new TreeNode(rows[i]);\r\n }\r\n\r\n for (let i = 0; i < l; i++) {\r\n // link all TreeNode objects\r\n node = nodeById[to(rows[i])];\r\n let parent = 0;\r\n const fromValue = from(node.row);\r\n if (!!fromValue && uniqIDs.indexOf(fromValue) > -1) {\r\n parent = fromValue;\r\n }\r\n node.parent = nodeById[parent];\r\n node.row['level'] = node.parent.row['level'] + 1;\r\n node.parent.children.push(node);\r\n }\r\n\r\n let resolvedRows: any[] = [];\r\n nodeById[0].flatten(function () {\r\n resolvedRows = [...resolvedRows, this.row];\r\n }, true);\r\n\r\n return resolvedRows;\r\n } else {\r\n return rows;\r\n }\r\n}\r\n\r\nclass TreeNode {\r\n public row: any;\r\n public parent: any;\r\n public children: any[];\r\n\r\n constructor(row: any | null = null) {\r\n if (!row) {\r\n row = {\r\n level: -1,\r\n treeStatus: 'expanded'\r\n };\r\n }\r\n this.row = row;\r\n this.parent = null;\r\n this.children = [];\r\n }\r\n\r\n flatten(f: any, recursive: boolean) {\r\n if (this.row['treeStatus'] === 'expanded') {\r\n for (let i = 0, l = this.children.length; i < l; i++) {\r\n const child = this.children[i];\r\n f.apply(child, Array.prototype.slice.call(arguments, 2));\r\n if (recursive) child.flatten.apply(child, arguments);\r\n }\r\n }\r\n }\r\n}\r\n","/**\r\n * Converts strings from something to camel case\r\n * http://stackoverflow.com/questions/10425287/convert-dash-separated-string-to-camelcase\r\n */\r\nexport function camelCase(str: string): string {\r\n // Replace special characters with a space\r\n str = str.replace(/[^a-zA-Z0-9 ]/g, ' ');\r\n // put a space before an uppercase letter\r\n str = str.replace(/([a-z](?=[A-Z]))/g, '$1 ');\r\n\r\n // Lower case first character and some other stuff\r\n str = str\r\n .replace(/([^a-zA-Z0-9 ])|^[0-9]+/g, '')\r\n .trim()\r\n .toLowerCase();\r\n\r\n // uppercase characters preceded by a space or number\r\n str = str.replace(/([ 0-9]+)([a-zA-Z])/g, function (a, b, c) {\r\n return b.trim() + c.toUpperCase();\r\n });\r\n\r\n return str;\r\n}\r\n\r\n/**\r\n * Converts strings from camel case to words\r\n * http://stackoverflow.com/questions/7225407/convert-camelcasetext-to-camel-case-text\r\n */\r\nexport function deCamelCase(str: string): string {\r\n return str.replace(/([A-Z])/g, match => ` ${match}`).replace(/^./, match => match.toUpperCase());\r\n}\r\n","/**\r\n * Creates a unique object id.\r\n * http://stackoverflow.com/questions/6248666/how-to-generate-short-uid-like-ax4j9z-in-js\r\n */\r\nexport function id() {\r\n return ('0000' + ((Math.random() * Math.pow(36, 4)) << 0).toString(36)).slice(-4);\r\n}\r\n","import { camelCase, deCamelCase } from './camel-case';\r\nimport { id } from './id';\r\nimport { getterForProp } from './column-prop-getters';\r\nimport { TableColumn } from '../types/table-column.type';\r\nimport { DataTableColumnDirective } from '../components/columns/column.directive';\r\n\r\n/**\r\n * Sets the column defaults\r\n */\r\nexport function setColumnDefaults(columns: TableColumn[]) {\r\n if (!columns) return;\r\n\r\n // Only one column should hold the tree view\r\n // Thus if multiple columns are provided with\r\n // isTreeColumn as true we take only the first one\r\n let treeColumnFound: boolean = false;\r\n\r\n for (const column of columns) {\r\n if (!column.$$id) {\r\n column.$$id = id();\r\n }\r\n\r\n // prop can be numeric; zero is valid not a missing prop\r\n // translate name => prop\r\n if (isNullOrUndefined(column.prop) && column.name) {\r\n column.prop = camelCase(column.name);\r\n }\r\n\r\n if (!column.$$valueGetter) {\r\n column.$$valueGetter = getterForProp(column.prop);\r\n }\r\n\r\n // format props if no name passed\r\n if (!isNullOrUndefined(column.prop) && isNullOrUndefined(column.name)) {\r\n column.name = deCamelCase(String(column.prop));\r\n }\r\n\r\n if (isNullOrUndefined(column.prop) && isNullOrUndefined(column.name)) {\r\n column.name = ''; // Fixes IE and Edge displaying `null`\r\n }\r\n\r\n if (!column.hasOwnProperty('resizeable')) {\r\n column.resizeable = true;\r\n }\r\n\r\n if (!column.hasOwnProperty('sortable')) {\r\n column.sortable = true;\r\n }\r\n\r\n if (!column.hasOwnProperty('draggable')) {\r\n column.draggable = true;\r\n }\r\n\r\n if (!column.hasOwnProperty('canAutoResize')) {\r\n column.canAutoResize = true;\r\n }\r\n\r\n if (!column.hasOwnProperty('width')) {\r\n column.width = 150;\r\n }\r\n\r\n if (!column.hasOwnProperty('isTreeColumn')) {\r\n column.isTreeColumn = false;\r\n } else {\r\n if (column.isTreeColumn && !treeColumnFound) {\r\n // If the first column with isTreeColumn is true found\r\n // we mark that treeCoulmn is found\r\n treeColumnFound = true;\r\n } else {\r\n // After that isTreeColumn property for any other column\r\n // will be set as false\r\n column.isTreeColumn = false;\r\n }\r\n }\r\n }\r\n}\r\n\r\nexport function isNullOrUndefined<T>(value: T | null | undefined): value is null | undefined {\r\n return value === null || value === undefined;\r\n}\r\n\r\n/**\r\n * Translates templates definitions to objects\r\n */\r\nexport function translateTemplates(templates: DataTableColumnDirective[]): any[] {\r\n const result: any[] = [];\r\n for (const temp of templates) {\r\n const col: any = {};\r\n\r\n const props = Object.getOwnPropertyNames(temp);\r\n for (const prop of props) {\r\n col[prop] = temp[prop];\r\n }\r\n\r\n if (temp.headerTemplate) {\r\n col.headerTemplate = temp.headerTemplate;\r\n }\r\n\r\n if (temp.cellTemplate) {\r\n col.cellTemplate = temp.cellTemplate;\r\n }\r\n\r\n if (temp.summaryFunc) {\r\n col.summaryFunc = temp.summaryFunc;\r\n }\r\n\r\n if (temp.summaryTemplate) {\r\n col.summaryTemplate = temp.summaryTemplate;\r\n }\r\n\r\n result.push(col);\r\n }\r\n\r\n return result;\r\n}\r\n","export enum ColumnMode {\r\n standard = 'standard',\r\n flex = 'flex',\r\n force = 'force'\r\n}\r\n","export enum SelectionType {\r\n single = 'single',\r\n multi = 'multi',\r\n multiClick = 'multiClick',\r\n cell = 'cell',\r\n checkbox = 'checkbox'\r\n}\r\n","export enum SortType {\r\n single = 'single',\r\n multi = 'multi'\r\n}\r\n","export enum ContextmenuType {\r\n header = 'header',\r\n body = 'body'\r\n}\r\n","import { Directive, TemplateRef } from '@angular/core';\r\n\r\n@Directive({ selector: '[ngx-datatable-header-template]' })\r\nexport class DataTableColumnHeaderDirective {\r\n constructor(public template: TemplateRef<any>) {}\r\n}\r\n","import { Directive, TemplateRef } from '@angular/core';\r\n\r\n@Directive({ selector: '[ngx-datatable-cell-template]' })\r\nexport class DataTableColumnCellDirective {\r\n constructor(public template: TemplateRef<any>) {}\r\n}\r\n","import { Directive, TemplateRef } from '@angular/core';\r\n\r\n@Directive({ selector: '[ngx-datatable-tree-toggle]' })\r\nexport class DataTableColumnCellTreeToggle {\r\n constructor(public template: TemplateRef<any>) {}\r\n}\r\n","import { Directive, TemplateRef, ContentChild, Input, OnChanges, SimpleChanges } from '@angular/core';\r\nimport { DataTableColumnHeaderDirective } from './column-header.directive';\r\nimport { DataTableColumnCellDirective } from './column-cell.directive';\r\nimport { DataTableColumnCellTreeToggle } from './tree.directive';\r\nimport { ColumnChangesService } from '../../services/column-changes.service';\r\nimport { TableColumnProp } from '../../types/table-column.type';\r\n\r\n@Directive({ selector: 'ngx-datatable-column' })\r\nexport class DataTableColumnDirective implements OnChanges {\r\n @Input() name: string;\r\n @Input() prop: TableColumnProp;\r\n @Input() frozenLeft: any;\r\n @Input() frozenRight: any;\r\n @Input() flexGrow: number;\r\n @Input() resizeable: boolean;\r\n @Input() comparator: any;\r\n @Input() pipe: any;\r\n @Input() sortable: boolean;\r\n @Input() draggable: boolean;\r\n @Input() canAutoResize: boolean;\r\n @Input() minWidth: number;\r\n @Input() width: number;\r\n @Input() maxWidth: number;\r\n @Input() checkboxable: boolean;\r\n @Input() headerCheckboxable: boolean;\r\n @Input() headerClass: string | ((data: any) => string | any);\r\n @Input() cellClass: string | ((data: any) => string | any);\r\n @Input() isTreeColumn: boolean;\r\n @Input() treeLevelIndent: number;\r\n @Input() summaryFunc: (cells: any[]) => any;\r\n @Input() summaryTemplate: TemplateRef<any>;\r\n\r\n @Input('cellTemplate')\r\n _cellTemplateInput: TemplateRef<any>;\r\n\r\n @ContentChild(DataTableColumnCellDirective, { read: TemplateRef, static: true })\r\n _cellTemplateQuery: TemplateRef<any>;\r\n\r\n get cellTemplate(): TemplateRef<any> {\r\n return this._cellTemplateInput || this._cellTemplateQuery;\r\n }\r\n\r\n @Input('headerTemplate')\r\n _headerTemplateInput: TemplateRef<any>;\r\n\r\n @ContentChild(DataTableColumnHeaderDirective, { read: TemplateRef, static: true })\r\n _headerTemplateQuery: TemplateRef<any>;\r\n\r\n get headerTemplate(): TemplateRef<any> {\r\n return this._headerTemplateInput || this._headerTemplateQuery;\r\n }\r\n\r\n @Input('treeToggleTemplate')\r\n _treeToggleTemplateInput: TemplateRef<any>;\r\n\r\n @ContentChild(DataTableColumnCellTreeToggle, { read: TemplateRef, static: true })\r\n _treeToggleTemplateQuery: TemplateRef<any>;\r\n\r\n get treeToggleTemplate(): TemplateRef<any> {\r\n return this._treeToggleTemplateInput || this._treeToggleTemplateQuery;\r\n }\r\n\r\n private isFirstChange = true;\r\n\r\n constructor(private columnChangesService: ColumnChangesService) {}\r\n\r\n ngOnChanges() {\r\n if (this.isFirstChange) {\r\n this.isFirstChange = false;\r\n } else {\r\n this.columnChangesService.onInputChange();\r\n }\r\n }\r\n}\r\n","import { Directive, TemplateRef } from '@angular/core';\r\n\r\n@Directive({\r\n selector: '[ngx-datatable-row-detail-template]'\r\n})\r\nexport class DatatableRowDetailTemplateDirective {\r\n constructor(public template: TemplateRef<any>) {}\r\n}\r\n","import { Input, Output, EventEmitter, Directive, TemplateRef, ContentChild } from '@angular/core';\r\nimport { DatatableRowDetailTemplateDirective } from './row-detail-template.directive';\r\n\r\n@Directive({ selector: 'ngx-datatable-row-detail' })\r\nexport class DatatableRowDetailDirective {\r\n /**\r\n * The detail row height is required especially\r\n * when virtual scroll is enabled.\r\n */\r\n @Input() rowHeight: number | ((row?: any, index?: number) => number) = 0;\r\n\r\n @Input('template')\r\n _templateInput: TemplateRef<any>;\r\n\r\n @ContentChild(DatatableRowDetailTemplateDirective, { read: TemplateRef, static: true })\r\n _templateQuery: TemplateRef<any>;\r\n\r\n get template(): TemplateRef<any> {\r\n return this._templateInput || this._templateQuery;\r\n }\r\n\r\n /**\r\n * Row detail row visbility was toggled.\r\n */\r\n @Output() toggle: EventEmitter<any> = new EventEmitter();\r\n\r\n /**\r\n * Toggle the expansion of the row\r\n */\r\n toggleExpandRow(row: any): void {\r\n this.toggle.emit({\r\n type: 'row',\r\n value: row\r\n });\r\n }\r\n\r\n /**\r\n * API method to expand all the rows.\r\n */\r\n expandAllRows(): void {\r\n this.toggle.emit({\r\n type: 'all',\r\n value: true\r\n });\r\n }\r\n\r\n /**\r\n * API method to collapse all the rows.\r\n */\r\n collapseAllRows(): void {\r\n this.toggle.emit({\r\n type: 'all',\r\n value: false\r\n });\r\n }\r\n}\r\n","import { Input, Directive, TemplateRef, ContentChild } from '@angular/core';\r\nimport { DataTableFooterTemplateDirective } from './footer-template.directive';\r\n\r\n@Directive({ selector: 'ngx-datatable-footer' })\r\nexport class DatatableFooterDirective {\r\n @Input() footerHeight: number;\r\n @Input() totalMessage: string;\r\n @Input() selectedMessage: string | boolean;\r\n @Input() pagerLeftArrowIcon: string;\r\n @Input() pagerRightArrowIcon: string;\r\n @Input() pagerPreviousIcon: string;\r\n @Input() pagerNextIcon: string;\r\n\r\n @Input('template')\r\n _templateInput: TemplateRef<any>;\r\n\r\n @ContentChild(DataTableFooterTemplateDirective, { read: TemplateRef })\r\n _templateQuery: TemplateRef<any>;\r\n\r\n get template(): TemplateRef<any> {\r\n return this._templateInput || this._templateQuery;\r\n }\r\n}\r\n","/**\r\n * Returns the columns by pin.\r\n */\r\nexport function columnsByPin(cols: any[]) {\r\n const ret: { left: any; center: any; right: any } = {\r\n left: [],\r\n center: [],\r\n right: []\r\n };\r\n\r\n if (cols) {\r\n for (const col of cols) {\r\n if (col.frozenLeft) {\r\n ret.left.push(col);\r\n } else if (col.frozenRight) {\r\n ret.right.push(col);\r\n } else {\r\n ret.center.push(col);\r\n }\r\n }\r\n }\r\n\r\n return ret;\r\n}\r\n\r\n/**\r\n * Returns the widths of all group sets of a column\r\n */\r\nexport function columnGroupWidths(groups: any, all: any) {\r\n return {\r\n left: columnTotalWidth(groups.left),\r\n center: columnTotalWidth(groups.center),\r\n right: columnTotalWidth(groups.right),\r\n total: Math.floor(columnTotalWidth(all))\r\n };\r\n}\r\n\r\n/**\r\n * Calculates the total width of all columns and their groups\r\n */\r\nexport function columnTotalWidth(columns: any[], prop?: string) {\r\n let totalWidth = 0;\r\n\r\n if (columns) {\r\n for (const c of columns) {\r\n const has = prop && c[prop];\r\n const width = has ? c[prop] : c.width;\r\n totalWidth = totalWidth + parseFloat(width);\r\n }\r\n }\r\n\r\n return totalWidth;\r\n}\r\n\r\n/**\r\n * Calculates the total width of all columns and their groups\r\n */\r\nexport function columnsTotalWidth(columns: any, prop?: any) {\r\n let totalWidth = 0;\r\n\r\n for (const column of columns) {\r\n const has = prop && column[prop];\r\n totalWidth = totalWidth + (has ? column[prop] : column.width);\r\n }\r\n\r\n return totalWidth;\r\n}\r\n\r\nexport function columnsByPinArr(val: any) {\r\n const colsByPinArr = [];\r\n const colsByPin = columnsByPin(val);\r\n\r\n colsByPinArr.push({ type: 'left', columns: colsByPin['left'] });\r\n colsByPinArr.push({ type: 'center', columns: colsByPin['center'] });\r\n colsByPinArr.push({ type: 'right', columns: colsByPin['right'] });\r\n\r\n return colsByPinArr;\r\n}\r\n","/**\r\n * This object contains the cache of the various row heights that are present inside\r\n * the data table. Its based on Fenwick tree data structure that helps with\r\n * querying sums that have time complexity of log n.\r\n *\r\n * Fenwick Tree Credits: http://petr-mitrichev.blogspot.com/2013/05/fenwick-tree-range-updates.html\r\n * https://github.com/mikolalysenko/fenwick-tree\r\n *\r\n */\r\nexport class RowHeightCache {\r\n /**\r\n * Tree Array stores the cumulative information of the row heights to perform efficient\r\n * range queries and updates. Currently the tree is initialized to the base row\r\n * height instead of the detail row height.\r\n */\r\n private treeArray: number[] = [];\r\n\r\n /**\r\n * Clear the Tree array.\r\n */\r\n clearCache(): void {\r\n this.treeArray = [];\r\n }\r\n\r\n /**\r\n * Initialize the Fenwick tree with row Heights.\r\n *\r\n * @param rows The array of rows which contain the expanded status.\r\n * @param rowHeight The row height.\r\n * @param detailRowHeight The detail row height.\r\n */\r\n initCache(details: any): void {\r\n const { rows, rowHeight, detailRowHeight, externalVirtual, rowCount, rowIndexes, rowExpansions } = details;\r\n const isFn = typeof rowHeight === 'function';\r\n const isDetailFn = typeof detailRowHeight === 'function';\r\n\r\n if (!isFn && isNaN(rowHeight)) {\r\n throw new Error(`Row Height cache initialization failed. Please ensure that 'rowHeight' is a\r\n valid number or function value: (${rowHeight}) when 'scrollbarV' is enabled.`);\r\n }\r\n\r\n // Add this additional guard in case detailRowHeight is set to 'auto' as it wont work.\r\n if (!isDetailFn && isNaN(detailRowHeight)) {\r\n throw new Error(`Row Height cache initialization failed. Please ensure that 'detailRowHeight' is a\r\n valid number or function value: (${detailRowHeight}) when 'scrollbarV' is enabled.`);\r\n }\r\n\r\n const n = externalVirtual ? rowCount : rows.length;\r\n this.treeArray = new Array(n);\r\n\r\n for (let i = 0; i < n; ++i) {\r\n this.treeArray[i] = 0;\r\n }\r\n\r\n for (let i = 0; i < n; ++i) {\r\n const row = rows[i];\r\n let currentRowHeight = rowHeight;\r\n if (isFn) {\r\n currentRowHeight = rowHeight(row);\r\n }\r\n\r\n // Add the detail row height to the already expanded rows.\r\n // This is useful for the table that goes through a filter or sort.\r\n const expanded = rowExpansions.has(row);\r\n if (row && expanded) {\r\n if (isDetailFn) {\r\n const index = rowIndexes.get(row);\r\n currentRowHeight += detailRowHeight(row, index);\r\n } else {\r\n currentRowHeight += detailRowHeight;\r\n }\r\n }\r\n\r\n this.update(i, currentRowHeight);\r\n }\r\n }\r\n\r\n /**\r\n * Given the Scrol