UNPKG

@siemens/ngx-datatable

Version:

ngx-datatable is an Angular table grid component for presenting large and complex data.

1,264 lines (1,238 loc) 290 kB
import * as i0 from '@angular/core'; import { Injectable, Inject, Directive, EventEmitter, HostBinding, Output, Input, HostListener, ContentChildren, Component, ChangeDetectionStrategy, TemplateRef, ContentChild, ViewContainerRef, ViewChild, SkipSelf, InjectionToken, inject, Injector, ElementRef, ViewEncapsulation, Optional, NgModule } from '@angular/core'; import * as i1 from '@angular/common'; import { DOCUMENT, CommonModule } from '@angular/common'; import { Subject, fromEvent, BehaviorSubject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { __decorate } from 'tslib'; /** * Gets the width of the scrollbar. Nesc for windows * http://stackoverflow.com/a/13382873/888165 */ class ScrollbarHelper { constructor(document) { this.document = document; this.width = this.getWidth(); } getWidth() { const outer = this.document.createElement('div'); outer.style.visibility = 'hidden'; outer.style.width = '100px'; outer.style.msOverflowStyle = 'scrollbar'; this.document.body.appendChild(outer); const widthNoScroll = outer.offsetWidth; outer.style.overflow = 'scroll'; const inner = this.document.createElement('div'); inner.style.width = '100%'; outer.appendChild(inner); const widthWithScroll = inner.offsetWidth; outer.parentNode.removeChild(outer); return widthNoScroll - widthWithScroll; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: ScrollbarHelper, deps: [{ token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: ScrollbarHelper }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: ScrollbarHelper, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: undefined, decorators: [{ type: Inject, args: [DOCUMENT] }] }] }); /** * Gets the width of the scrollbar. Nesc for windows * http://stackoverflow.com/a/13382873/888165 */ class DimensionsHelper { getDimensions(element) { return element.getBoundingClientRect(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DimensionsHelper, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DimensionsHelper }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DimensionsHelper, decorators: [{ type: Injectable }] }); /** * service to make DatatableComponent aware of changes to * input bindings of DataTableColumnDirective */ class ColumnChangesService { constructor() { this.columnInputChanges = new Subject(); } get columnInputChanges$() { return this.columnInputChanges.asObservable(); } onInputChange() { this.columnInputChanges.next(undefined); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: ColumnChangesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: ColumnChangesService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: ColumnChangesService, decorators: [{ type: Injectable }] }); class DataTableFooterTemplateDirective { constructor(template) { this.template = template; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DataTableFooterTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.0.6", type: DataTableFooterTemplateDirective, selector: "[ngx-datatable-footer-template]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DataTableFooterTemplateDirective, decorators: [{ type: Directive, args: [{ selector: '[ngx-datatable-footer-template]' }] }], ctorParameters: () => [{ type: i0.TemplateRef }] }); /** * Visibility Observer Directive * * Usage: * * <div * visibilityObserver * (visible)="onVisible($event)"> * </div> * */ class VisibilityDirective { constructor(element, zone) { this.element = element; this.zone = zone; this.isVisible = false; this.visible = new EventEmitter(); } ngOnInit() { this.runCheck(); } ngOnDestroy() { clearTimeout(this.timeout); } onVisibilityChange() { // trigger zone recalc for columns this.zone.run(() => { this.isVisible = true; this.visible.emit(true); }); } runCheck() { const check = () => { // https://davidwalsh.name/offsetheight-visibility const { offsetHeight, offsetWidth } = this.element.nativeElement; if (offsetHeight && offsetWidth) { clearTimeout(this.timeout); this.onVisibilityChange(); } else { clearTimeout(this.timeout); this.zone.runOutsideAngular(() => { this.timeout = setTimeout(() => check(), 50); }); } }; this.timeout = setTimeout(() => check()); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: VisibilityDirective, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.0.6", type: VisibilityDirective, selector: "[visibilityObserver]", outputs: { visible: "visible" }, host: { properties: { "class.visible": "this.isVisible" } }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: VisibilityDirective, decorators: [{ type: Directive, args: [{ selector: '[visibilityObserver]' }] }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.NgZone }], propDecorators: { isVisible: [{ type: HostBinding, args: ['class.visible'] }], visible: [{ type: Output }] } }); /** * Draggable Directive for Angular2 * * Inspiration: * https://github.com/AngularClass/angular2-examples/blob/master/rx-draggable/directives/draggable.ts * http://stackoverflow.com/questions/35662530/how-to-implement-drag-and-drop-in-angular2 * */ class DraggableDirective { constructor(element) { this.dragX = true; this.dragY = true; this.dragStart = new EventEmitter(); this.dragging = new EventEmitter(); this.dragEnd = new EventEmitter(); this.isDragging = false; this.element = element.nativeElement; } ngOnChanges(changes) { if (changes.dragEventTarget && changes.dragEventTarget.currentValue && this.dragModel.dragging) { this.onMousedown(changes.dragEventTarget.currentValue); } } ngOnDestroy() { this._destroySubscription(); } onMouseup(event) { if (!this.isDragging) { return; } this.isDragging = false; this.element.classList.remove('dragging'); if (this.subscription) { this._destroySubscription(); this.dragEnd.emit({ event, element: this.element, model: this.dragModel }); } } onMousedown(event) { // we only want to drag the inner header text const isDragElm = event.target.classList.contains('draggable'); if (isDragElm && (this.dragX || this.dragY)) { event.preventDefault(); this.isDragging = true; const mouseDownPos = { x: event.clientX, y: event.clientY }; const mouseup = fromEvent(document, 'mouseup'); this.subscription = mouseup.subscribe((ev) => this.onMouseup(ev)); const mouseMoveSub = fromEvent(document, 'mousemove') .pipe(takeUntil(mouseup)) .subscribe((ev) => this.move(ev, mouseDownPos)); this.subscription.add(mouseMoveSub); this.dragStart.emit({ event, element: this.element, model: this.dragModel }); } } move(event, mouseDownPos) { if (!this.isDragging) { return; } const x = event.clientX - mouseDownPos.x; const y = event.clientY - mouseDownPos.y; if (this.dragX) { this.element.style.left = `${x}px`; } if (this.dragY) { this.element.style.top = `${y}px`; } this.element.classList.add('dragging'); this.dragging.emit({ event, element: this.element, model: this.dragModel }); } _destroySubscription() { if (this.subscription) { this.subscription.unsubscribe(); this.subscription = undefined; } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DraggableDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.0.6", type: DraggableDirective, selector: "[draggable]", inputs: { dragEventTarget: "dragEventTarget", dragModel: "dragModel", dragX: "dragX", dragY: "dragY" }, outputs: { dragStart: "dragStart", dragging: "dragging", dragEnd: "dragEnd" }, usesOnChanges: true, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DraggableDirective, decorators: [{ type: Directive, args: [{ selector: '[draggable]' }] }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { dragEventTarget: [{ type: Input }], dragModel: [{ type: Input }], dragX: [{ type: Input }], dragY: [{ type: Input }], dragStart: [{ type: Output }], dragging: [{ type: Output }], dragEnd: [{ type: Output }] } }); class ResizeableDirective { constructor(element, renderer) { this.renderer = renderer; this.resizeEnabled = true; this.resize = new EventEmitter(); this.resizing = new EventEmitter(); this.element = element.nativeElement; } ngAfterViewInit() { const renderer2 = this.renderer; this.resizeHandle = renderer2.createElement('span'); if (this.resizeEnabled) { renderer2.addClass(this.resizeHandle, 'resize-handle'); } else { renderer2.addClass(this.resizeHandle, 'resize-handle--not-resizable'); } renderer2.appendChild(this.element, this.resizeHandle); } ngOnDestroy() { this._destroySubscription(); if (this.renderer.destroyNode) { this.renderer.destroyNode(this.resizeHandle); } else if (this.resizeHandle) { this.renderer.removeChild(this.renderer.parentNode(this.resizeHandle), this.resizeHandle); } } onMouseup() { if (this.subscription && !this.subscription.closed) { this._destroySubscription(); this.resize.emit(this.element.clientWidth); } } onMousedown(event) { const isHandle = event.target.classList.contains('resize-handle'); const initialWidth = this.element.clientWidth; const mouseDownScreenX = event.screenX; if (isHandle) { event.stopPropagation(); const mouseup = fromEvent(document, 'mouseup'); this.subscription = mouseup.subscribe((ev) => this.onMouseup()); const mouseMoveSub = fromEvent(document, 'mousemove') .pipe(takeUntil(mouseup)) .subscribe((e) => this.move(e, initialWidth, mouseDownScreenX)); this.subscription.add(mouseMoveSub); } } move(event, initialWidth, mouseDownScreenX) { const movementX = event.screenX - mouseDownScreenX; const newWidth = initialWidth + movementX; this.resizing.emit(newWidth); } _destroySubscription() { if (this.subscription) { this.subscription.unsubscribe(); this.subscription = undefined; } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: ResizeableDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.0.6", type: ResizeableDirective, selector: "[resizeable]", inputs: { resizeEnabled: "resizeEnabled", minWidth: "minWidth", maxWidth: "maxWidth" }, outputs: { resize: "resize", resizing: "resizing" }, host: { listeners: { "mousedown": "onMousedown($event)" }, properties: { "class.resizeable": "this.resizeEnabled" } }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: ResizeableDirective, decorators: [{ type: Directive, args: [{ selector: '[resizeable]' }] }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { resizeEnabled: [{ type: HostBinding, args: ['class.resizeable'] }, { type: Input }], minWidth: [{ type: Input }], maxWidth: [{ type: Input }], resize: [{ type: Output }], resizing: [{ type: Output }], onMousedown: [{ type: HostListener, args: ['mousedown', ['$event']] }] } }); class OrderableDirective { constructor(differs, document) { this.document = document; this.reorder = new EventEmitter(); this.targetChanged = new EventEmitter(); this.differ = differs.find({}).create(); } ngAfterContentInit() { // HACK: Investigate Better Way this.updateSubscriptions(); this.draggables.changes.subscribe(this.updateSubscriptions.bind(this)); } ngOnDestroy() { this.draggables.forEach(d => { d.dragStart.unsubscribe(); d.dragging.unsubscribe(); d.dragEnd.unsubscribe(); }); } updateSubscriptions() { const diffs = this.differ.diff(this.createMapDiffs()); if (diffs) { const subscribe = ({ currentValue, previousValue }) => { unsubscribe({ previousValue }); if (currentValue) { currentValue.dragStart.subscribe(this.onDragStart.bind(this)); currentValue.dragging.subscribe(this.onDragging.bind(this)); currentValue.dragEnd.subscribe(this.onDragEnd.bind(this)); } }; const unsubscribe = ({ previousValue }) => { if (previousValue) { previousValue.dragStart.unsubscribe(); previousValue.dragging.unsubscribe(); previousValue.dragEnd.unsubscribe(); } }; diffs.forEachAddedItem(subscribe); // diffs.forEachChangedItem(subscribe.bind(this)); diffs.forEachRemovedItem(unsubscribe); } } onDragStart() { this.positions = {}; let i = 0; for (const dragger of this.draggables.toArray()) { const elm = dragger.element; const left = parseInt(elm.offsetLeft.toString(), 10); this.positions[dragger.dragModel.$$id] = { left, right: left + parseInt(elm.offsetWidth.toString(), 10), index: i++, element: elm }; } } onDragging({ element, model, event }) { const prevPos = this.positions[model.$$id]; const target = this.isTarget(model, event); if (target) { if (this.lastDraggingIndex !== target.i) { this.targetChanged.emit({ prevIndex: this.lastDraggingIndex, newIndex: target.i, initialIndex: prevPos.index }); this.lastDraggingIndex = target.i; } } else if (this.lastDraggingIndex !== prevPos.index) { this.targetChanged.emit({ prevIndex: this.lastDraggingIndex, initialIndex: prevPos.index }); this.lastDraggingIndex = prevPos.index; } } onDragEnd({ element, model, event }) { const prevPos = this.positions[model.$$id]; const target = this.isTarget(model, event); if (target) { this.reorder.emit({ prevIndex: prevPos.index, newIndex: target.i, model }); } this.lastDraggingIndex = undefined; element.style.left = 'auto'; } isTarget(model, event) { let i = 0; const x = event.x || event.clientX; const y = event.y || event.clientY; const targets = this.document.elementsFromPoint(x, y); // eslint-disable-next-line guard-for-in for (const id in this.positions) { // current column position which throws event. const pos = this.positions[id]; // since we drag the inner span, we need to find it in the elements at the cursor if (model.$$id !== id && targets.find((el) => el === pos.element)) { return { pos, i }; } i++; } } createMapDiffs() { return this.draggables.toArray().reduce((acc, curr) => { acc[curr.dragModel.$$id] = curr; return acc; }, {}); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: OrderableDirective, deps: [{ token: i0.KeyValueDiffers }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.0.6", type: OrderableDirective, selector: "[orderable]", outputs: { reorder: "reorder", targetChanged: "targetChanged" }, queries: [{ propertyName: "draggables", predicate: DraggableDirective, descendants: true }], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: OrderableDirective, decorators: [{ type: Directive, args: [{ selector: '[orderable]' }] }], ctorParameters: () => [{ type: i0.KeyValueDiffers }, { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT] }] }], propDecorators: { reorder: [{ type: Output }], targetChanged: [{ type: Output }], draggables: [{ type: ContentChildren, args: [DraggableDirective, { descendants: true }] }] } }); class LongPressDirective { constructor() { this.pressEnabled = true; this.duration = 500; this.longPressStart = new EventEmitter(); this.longPressing = new EventEmitter(); this.longPressEnd = new EventEmitter(); this.mouseX = 0; this.mouseY = 0; } get press() { return this.pressing; } get isLongPress() { return this.isLongPressing; } onMouseDown(event) { // don't do right/middle clicks if (event.which !== 1 || !this.pressEnabled) { return; } // don't start drag if its on resize handle const target = event.target; if (target.classList.contains('resize-handle')) { return; } this.mouseX = event.clientX; this.mouseY = event.clientY; this.pressing = true; this.isLongPressing = false; const mouseup = fromEvent(document, 'mouseup'); this.subscription = mouseup.subscribe((ev) => this.onMouseup()); this.timeout = setTimeout(() => { this.isLongPressing = true; this.longPressStart.emit({ event, model: this.pressModel }); this.subscription.add(fromEvent(document, 'mousemove') .pipe(takeUntil(mouseup)) .subscribe((mouseEvent) => this.onMouseMove(mouseEvent))); this.loop(event); }, this.duration); this.loop(event); } onMouseMove(event) { if (this.pressing && !this.isLongPressing) { const xThres = Math.abs(event.clientX - this.mouseX) > 10; const yThres = Math.abs(event.clientY - this.mouseY) > 10; if (xThres || yThres) { this.endPress(); } } } loop(event) { if (this.isLongPressing) { this.timeout = setTimeout(() => { this.longPressing.emit({ event, model: this.pressModel }); this.loop(event); }, 50); } } endPress() { clearTimeout(this.timeout); this.isLongPressing = false; this.pressing = false; this._destroySubscription(); this.longPressEnd.emit({ model: this.pressModel }); } onMouseup() { this.endPress(); } ngOnDestroy() { clearTimeout(this.timeout); this._destroySubscription(); } _destroySubscription() { if (this.subscription) { this.subscription.unsubscribe(); this.subscription = undefined; } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: LongPressDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.0.6", type: LongPressDirective, selector: "[long-press]", inputs: { pressEnabled: "pressEnabled", pressModel: "pressModel", duration: "duration" }, outputs: { longPressStart: "longPressStart", longPressing: "longPressing", longPressEnd: "longPressEnd" }, host: { listeners: { "mousedown": "onMouseDown($event)" }, properties: { "class.press": "this.press", "class.longpress": "this.isLongPress" } }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: LongPressDirective, decorators: [{ type: Directive, args: [{ selector: '[long-press]' }] }], propDecorators: { pressEnabled: [{ type: Input }], pressModel: [{ type: Input }], duration: [{ type: Input }], longPressStart: [{ type: Output }], longPressing: [{ type: Output }], longPressEnd: [{ type: Output }], press: [{ type: HostBinding, args: ['class.press'] }], isLongPress: [{ type: HostBinding, args: ['class.longpress'] }], onMouseDown: [{ type: HostListener, args: ['mousedown', ['$event']] }] } }); class ScrollerComponent { constructor(ngZone, element, renderer) { this.ngZone = ngZone; this.renderer = renderer; this.scrollbarV = false; this.scrollbarH = false; this.scroll = new EventEmitter(); this.scrollYPos = 0; this.scrollXPos = 0; this.prevScrollYPos = 0; this.prevScrollXPos = 0; this._scrollEventListener = null; this.element = element.nativeElement; } ngOnInit() { // manual bind so we don't always listen if (this.scrollbarV || this.scrollbarH) { const renderer = this.renderer; this.parentElement = renderer.parentNode(renderer.parentNode(this.element)); this._scrollEventListener = this.onScrolled.bind(this); this.parentElement.addEventListener('scroll', this._scrollEventListener); } } ngOnDestroy() { if (this._scrollEventListener) { this.parentElement.removeEventListener('scroll', this._scrollEventListener); this._scrollEventListener = null; } } setOffset(offsetY) { if (this.parentElement) { this.parentElement.scrollTop = offsetY; } } onScrolled(event) { const dom = event.currentTarget; requestAnimationFrame(() => { this.scrollYPos = dom.scrollTop; this.scrollXPos = dom.scrollLeft; this.updateOffset(); }); } updateOffset() { let direction; if (this.scrollYPos < this.prevScrollYPos) { direction = 'down'; } else if (this.scrollYPos > this.prevScrollYPos) { direction = 'up'; } this.scroll.emit({ direction, scrollYPos: this.scrollYPos, scrollXPos: this.scrollXPos }); this.prevScrollYPos = this.scrollYPos; this.prevScrollXPos = this.scrollXPos; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: ScrollerComponent, deps: [{ token: i0.NgZone }, { token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.0.6", type: ScrollerComponent, selector: "datatable-scroller", inputs: { scrollbarV: "scrollbarV", scrollbarH: "scrollbarH", scrollHeight: "scrollHeight", scrollWidth: "scrollWidth" }, outputs: { scroll: "scroll" }, host: { properties: { "style.height.px": "this.scrollHeight", "style.width.px": "this.scrollWidth" }, classAttribute: "datatable-scroll" }, ngImport: i0, template: ` <ng-content></ng-content> `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: ScrollerComponent, decorators: [{ type: Component, args: [{ selector: 'datatable-scroller', template: ` <ng-content></ng-content> `, host: { class: 'datatable-scroll' }, changeDetection: ChangeDetectionStrategy.OnPush }] }], ctorParameters: () => [{ type: i0.NgZone }, { type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { scrollbarV: [{ type: Input }], scrollbarH: [{ type: Input }], scrollHeight: [{ type: HostBinding, args: ['style.height.px'] }, { type: Input }], scrollWidth: [{ type: HostBinding, args: ['style.width.px'] }, { type: Input }], scroll: [{ type: Output }] } }); class DatatableGroupHeaderTemplateDirective { constructor(template) { this.template = template; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DatatableGroupHeaderTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.0.6", type: DatatableGroupHeaderTemplateDirective, selector: "[ngx-datatable-group-header-template]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DatatableGroupHeaderTemplateDirective, decorators: [{ type: Directive, args: [{ selector: '[ngx-datatable-group-header-template]' }] }], ctorParameters: () => [{ type: i0.TemplateRef }] }); class DatatableGroupHeaderDirective { constructor() { /** * Row height is required when virtual scroll is enabled. */ this.rowHeight = 0; /** * Show checkbox at group header to select all rows of the group. */ this.checkboxable = false; /** * Track toggling of group visibility */ this.toggle = new EventEmitter(); } get template() { return this._templateInput || this._templateQuery; } /** * Toggle the expansion of a group */ toggleExpandGroup(group) { this.toggle.emit({ type: 'group', value: group }); } /** * Expand all groups */ expandAllGroups() { this.toggle.emit({ type: 'all', value: true }); } /** * Collapse all groups */ collapseAllGroups() { this.toggle.emit({ type: 'all', value: false }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DatatableGroupHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.0.6", type: DatatableGroupHeaderDirective, selector: "ngx-datatable-group-header", inputs: { rowHeight: "rowHeight", checkboxable: "checkboxable", _templateInput: ["template", "_templateInput"] }, outputs: { toggle: "toggle" }, queries: [{ propertyName: "_templateQuery", first: true, predicate: DatatableGroupHeaderTemplateDirective, descendants: true, read: TemplateRef, static: true }], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DatatableGroupHeaderDirective, decorators: [{ type: Directive, args: [{ selector: 'ngx-datatable-group-header' }] }], propDecorators: { rowHeight: [{ type: Input }], checkboxable: [{ type: Input }], _templateInput: [{ type: Input, args: ['template'] }], _templateQuery: [{ type: ContentChild, args: [DatatableGroupHeaderTemplateDirective, { read: TemplateRef, static: true }] }], toggle: [{ type: Output }] } }); /** * Always returns the empty string '' */ function emptyStringGetter() { return ''; } /** * Returns the appropriate getter function for this kind of prop. * If prop == null, returns the emptyStringGetter. */ function getterForProp(prop) { if (prop == null) { return emptyStringGetter; } if (typeof prop === 'number') { return numericIndexGetter; } else { // deep or simple if (prop.indexOf('.') !== -1) { return deepValueGetter; } else { return shallowValueGetter; } } } /** * Returns the value at this numeric index. * @param row array of values * @param index numeric index * @returns any or '' if invalid index */ function numericIndexGetter(row, index) { if (row == null) { return ''; } // mimic behavior of deepValueGetter if (!row || index == null) { return row; } const value = row[index]; if (value == null) { return ''; } return value; } /** * Returns the value of a field. * (more efficient than deepValueGetter) * @param obj object containing the field * @param fieldName field name string */ function shallowValueGetter(obj, fieldName) { if (obj == null) { return ''; } if (!obj || !fieldName) { return obj; } const value = obj[fieldName]; if (value == null) { return ''; } return value; } /** * Returns a deep object given a string. zoo['animal.type'] */ function deepValueGetter(obj, path) { if (obj == null) { return ''; } if (!obj || !path) { return obj; } // check if path matches a root-level field // { "a.b.c": 123 } let current = obj[path]; if (current !== undefined) { return current; } current = obj; const splits = path.split('.'); if (splits.length) { for (const split of splits) { current = current[split]; // if found undefined, return empty string if (current === undefined || current === null) { return ''; } } } return current; } function optionalGetterForProp(prop) { return prop && (row => getterForProp(prop)(row, prop)); } /** * This functions rearrange items by their parents * Also sets the level value to each of the items * * Note: Expecting each item has a property called parentId * Note: This algorithm will fail if a list has two or more items with same ID * NOTE: This algorithm will fail if there is a deadlock of relationship * * For example, * * Input * * id -> parent * 1 -> 0 * 2 -> 0 * 3 -> 1 * 4 -> 1 * 5 -> 2 * 7 -> 8 * 6 -> 3 * * * Output * id -> level * 1 -> 0 * --3 -> 1 * ----6 -> 2 * --4 -> 1 * 2 -> 0 * --5 -> 1 * 7 -> 8 * * * @param rows * */ function groupRowsByParents(rows, from, to) { if (from && to) { const nodeById = {}; const l = rows.length; let node = null; nodeById[0] = new TreeNode(); // that's the root node const uniqIDs = rows.reduce((arr, item) => { const toValue = to(item); if (arr.indexOf(toValue) === -1) { arr.push(toValue); } return arr; }, []); for (let i = 0; i < l; i++) { // make TreeNode objects for each item nodeById[to(rows[i])] = new TreeNode(rows[i]); } for (let i = 0; i < l; i++) { // link all TreeNode objects node = nodeById[to(rows[i])]; let parent = 0; const fromValue = from(node.row); if (!!fromValue && uniqIDs.indexOf(fromValue) > -1) { parent = fromValue; } node.parent = nodeById[parent]; node.row.level = node.parent.row.level + 1; node.parent.children.push(node); } let resolvedRows = []; nodeById[0].flatten(function () { resolvedRows = [...resolvedRows, this.row]; }, true); return resolvedRows; } else { return rows; } } class TreeNode { constructor(row = null) { if (!row) { row = { level: -1, treeStatus: 'expanded' }; } this.row = row; this.parent = null; this.children = []; } flatten(f, recursive) { if (this.row.treeStatus === 'expanded') { for (let i = 0, l = this.children.length; i < l; i++) { const child = this.children[i]; f.apply(child, Array.prototype.slice.call(arguments, 2)); if (recursive) { child.flatten.apply(child, arguments); } } } } } /** * Converts strings from something to camel case * http://stackoverflow.com/questions/10425287/convert-dash-separated-string-to-camelcase */ function camelCase(str) { // Replace special characters with a space str = str.replace(/[^a-zA-Z0-9 ]/g, ' '); // put a space before an uppercase letter str = str.replace(/([a-z](?=[A-Z]))/g, '$1 '); // Lower case first character and some other stuff str = str .replace(/([^a-zA-Z0-9 ])|^[0-9]+/g, '') .trim() .toLowerCase(); // uppercase characters preceded by a space or number str = str.replace(/([ 0-9]+)([a-zA-Z])/g, function (a, b, c) { return b.trim() + c.toUpperCase(); }); return str; } /** * Converts strings from camel case to words * http://stackoverflow.com/questions/7225407/convert-camelcasetext-to-camel-case-text */ function deCamelCase(str) { return str.replace(/([A-Z])/g, match => ` ${match}`).replace(/^./, match => match.toUpperCase()); } /** * Creates a unique object id. * http://stackoverflow.com/questions/6248666/how-to-generate-short-uid-like-ax4j9z-in-js */ function id() { return ('0000' + ((Math.random() * Math.pow(36, 4)) << 0).toString(36)).slice(-4); } /** * Sets the column defaults */ function setColumnDefaults(columns) { if (!columns) { return; } // Only one column should hold the tree view // Thus if multiple columns are provided with // isTreeColumn as true we take only the first one let treeColumnFound = false; for (const column of columns) { if (!column.$$id) { column.$$id = id(); } // prop can be numeric; zero is valid not a missing prop // translate name => prop if (isNullOrUndefined(column.prop) && column.name) { column.prop = camelCase(column.name); } if (!column.$$valueGetter) { column.$$valueGetter = getterForProp(column.prop); } // format props if no name passed if (!isNullOrUndefined(column.prop) && isNullOrUndefined(column.name)) { column.name = deCamelCase(String(column.prop)); } if (isNullOrUndefined(column.prop) && isNullOrUndefined(column.name)) { column.name = ''; // Fixes IE and Edge displaying `null` } if (!column.hasOwnProperty('resizeable')) { column.resizeable = true; } if (!column.hasOwnProperty('sortable')) { column.sortable = true; } if (!column.hasOwnProperty('draggable')) { column.draggable = true; } if (!column.hasOwnProperty('canAutoResize')) { column.canAutoResize = true; } if (!column.hasOwnProperty('width')) { column.width = 150; } if (!column.hasOwnProperty('isTreeColumn')) { column.isTreeColumn = false; } else { if (column.isTreeColumn && !treeColumnFound) { // If the first column with isTreeColumn is true found // we mark that treeCoulmn is found treeColumnFound = true; } else { // After that isTreeColumn property for any other column // will be set as false column.isTreeColumn = false; } } } } function isNullOrUndefined(value) { return value === null || value === undefined; } /** * Translates templates definitions to objects */ function translateTemplates(templates) { const result = []; for (const temp of templates) { const col = {}; const props = Object.getOwnPropertyNames(temp); for (const prop of props) { col[prop] = temp[prop]; } if (temp.headerTemplate) { col.headerTemplate = temp.headerTemplate; } if (temp.cellTemplate) { col.cellTemplate = temp.cellTemplate; } if (temp.ghostCellTemplate) { col.ghostCellTemplate = temp.ghostCellTemplate; } if (temp.summaryFunc) { col.summaryFunc = temp.summaryFunc; } if (temp.summaryTemplate) { col.summaryTemplate = temp.summaryTemplate; } result.push(col); } return result; } var ColumnMode; (function (ColumnMode) { ColumnMode["standard"] = "standard"; ColumnMode["flex"] = "flex"; ColumnMode["force"] = "force"; })(ColumnMode || (ColumnMode = {})); var SelectionType; (function (SelectionType) { SelectionType["single"] = "single"; SelectionType["multi"] = "multi"; SelectionType["multiClick"] = "multiClick"; SelectionType["cell"] = "cell"; SelectionType["checkbox"] = "checkbox"; })(SelectionType || (SelectionType = {})); var SortType; (function (SortType) { SortType["single"] = "single"; SortType["multi"] = "multi"; })(SortType || (SortType = {})); var ContextmenuType; (function (ContextmenuType) { ContextmenuType["header"] = "header"; ContextmenuType["body"] = "body"; })(ContextmenuType || (ContextmenuType = {})); class DataTableColumnHeaderDirective { constructor(template) { this.template = template; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DataTableColumnHeaderDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.0.6", type: DataTableColumnHeaderDirective, selector: "[ngx-datatable-header-template]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DataTableColumnHeaderDirective, decorators: [{ type: Directive, args: [{ selector: '[ngx-datatable-header-template]' }] }], ctorParameters: () => [{ type: i0.TemplateRef }] }); class DataTableColumnCellDirective { constructor(template) { this.template = template; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DataTableColumnCellDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.0.6", type: DataTableColumnCellDirective, selector: "[ngx-datatable-cell-template]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DataTableColumnCellDirective, decorators: [{ type: Directive, args: [{ selector: '[ngx-datatable-cell-template]' }] }], ctorParameters: () => [{ type: i0.TemplateRef }] }); class DataTableColumnCellTreeToggle { constructor(template) { this.template = template; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DataTableColumnCellTreeToggle, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.0.6", type: DataTableColumnCellTreeToggle, selector: "[ngx-datatable-tree-toggle]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DataTableColumnCellTreeToggle, decorators: [{ type: Directive, args: [{ selector: '[ngx-datatable-tree-toggle]' }] }], ctorParameters: () => [{ type: i0.TemplateRef }] }); class DataTableColumnGhostCellDirective { constructor(template) { this.template = template; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DataTableColumnGhostCellDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.0.6", type: DataTableColumnGhostCellDirective, selector: "[ngx-datatable-ghost-cell-template]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DataTableColumnGhostCellDirective, decorators: [{ type: Directive, args: [{ selector: '[ngx-datatable-ghost-cell-template]' }] }], ctorParameters: () => [{ type: i0.TemplateRef }] }); class DataTableColumnDirective { get cellTemplate() { return this._cellTemplateInput || this._cellTemplateQuery; } get headerTemplate() { return this._headerTemplateInput || this._headerTemplateQuery; } get treeToggleTemplate() { return this._treeToggleTemplateInput || this._treeToggleTemplateQuery; } get ghostCellTemplate() { return this._ghostCellTemplateInput || this._ghostCellTemplateQuery; } constructor(columnChangesService) { this.columnChangesService = columnChangesService; this.isFirstChange = true; } ngOnChanges() { if (this.isFirstChange) { this.isFirstChange = false; } else { this.columnChangesService.onInputChange(); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DataTableColumnDirective, deps: [{ token: ColumnChangesService }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.0.6", type: DataTableColumnDirective, selector: "ngx-datatable-column", inputs: { name: "name", prop: "prop", frozenLeft: "frozenLeft", frozenRight: "frozenRight", flexGrow: "flexGrow", resizeable: "resizeable", comparator: "comparator", pipe: "pipe", sortable: "sortable", draggable: "draggable", canAutoResize: "canAutoResize", minWidth: "minWidth", width: "width", maxWidth: "maxWidth", checkboxable: "checkboxable", headerCheckboxable: "headerCheckboxable", headerClass: "headerClass", cellClass: "cellClass", isTreeColumn: "isTreeColumn", treeLevelIndent: "treeLevelIndent", summaryFunc: "summaryFunc", summaryTemplate: "summaryTemplate", _cellTemplateInput: ["cellTemplate", "_cellTemplateInput"], _headerTemplateInput: ["headerTemplate", "_headerTemplateInput"], _treeToggleTemplateInput: ["treeToggleTemplate", "_treeToggleTemplateInput"], _ghostCellTemplateInput: ["ghostCellTemplate", "_ghostCellTemplateInput"] }, queries: [{ propertyName: "_cellTemplateQuery", first: true, predicate: DataTableColumnCellDirective, descendants: true, read: TemplateRef, static: true }, { propertyName: "_headerTemplateQuery", first: true, predicate: DataTableColumnHeaderDirective, descendants: true, read: TemplateRef, static: true }, { propertyName: "_treeToggleTemplateQuery", first: true, predicate: DataTableColumnCellTreeToggle, descendants: true, read: TemplateRef, static: true }, { propertyName: "_ghostCellTemplateQuery", first: true, predicate: DataTableColumnGhostCellDirective, descendants: true, read: TemplateRef, static: true }], usesOnChanges: true, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: DataTableColumnDirective, decorators: [{ type: Directive, args: [{ selector: 'ngx-datatable-column' }] }], ctorParameters: () => [{ type: ColumnChangesService }], propDecorators: { name: [{ type: Input }], prop: [{ type: Input }], frozenLeft: [{ type: Input }], frozenRight: [{ type: Input }], flexGrow: [{ type: Input }], resizeable: [{ type: Input }], comparator: [{ type: Input }], pipe: [{ type: Input }], sortable: [{ type: Input }], draggable: [{ type: Input }], canAutoResize: [{ type: Input }], minWidth: [{ type: Input }], width: [{ type: Input }], maxWidth: [{ type: Input }], checkboxable: [{ type: Input }], headerCheckboxable: [{ type: Input }], headerClass: [{ type: Input }], cellClass: [{ type: Input }], isTreeColumn: [{ type: Input }], treeLevelIndent: [{ type: Input }], summaryFunc: [{ type: Input }], summaryTemplate: [{ type: Input }], _cellTemplateInput: [{ type: Input, args: ['cellTemplate'] }], _cellTemplateQuery: [{ type: ContentChild, args: [DataTableColumnCellDirective, { read: TemplateRef, static: true }] }], _headerTemplateInput: [{ type: Input, args: ['headerTemplate'] }], _headerTemplateQuery: [{ type: ContentChild, args: [DataTableColumnHeaderDirective, { read: TemplateRef, static: true }] }], _treeToggleTemplateInput: [{ type: Input, args: ['treeToggleTemplate'] }], _treeToggleTemplateQuery: [{ type: ContentChild, args: [DataTableColumnCellTreeToggle, { read: TemplateRef, static: true }] }], _ghostCellTemplateInput: [{ type: Input, a