UNPKG

novo-elements

Version:

Bullhorn's NOVO Element Repository for Angular 2

249 lines (212 loc) 11.1 kB
import { Directive, ElementRef, Input, Renderer2, HostBinding, Component, ChangeDetectionStrategy, ChangeDetectorRef, Optional, OnInit, OnDestroy, HostListener, ViewChild } from '@angular/core'; import { CdkCell, CdkCellDef, CdkColumnDef, CdkHeaderCell, CdkHeaderCellDef, DataSource } from '@angular/cdk/table'; import { Subscription } from 'rxjs/Subscription'; import { NovoSelection } from './sort'; import { SimpleTableColumn, SimpleTableActionColumn, SimpleTableActionColumnOption } from './interfaces'; import { Helpers } from '../../utils/Helpers'; import { NovoLabelService } from '../../services/novo-label-service'; /** Workaround for https://github.com/angular/angular/issues/17849 */ export const _NovoCellDef = CdkCellDef; export const _NovoHeaderCellDef = CdkHeaderCellDef; export const _NovoColumnDef = CdkColumnDef; export const _NovoHeaderCell = CdkHeaderCell; export const _NovoCell = CdkCell; @Directive({ selector: '[novoSimpleCellDef]', providers: [{ provide: CdkCellDef, useExisting: NovoSimpleCellDef }] }) export class NovoSimpleCellDef extends _NovoCellDef { } @Directive({ selector: '[novoSimpleHeaderCellDef]', providers: [{ provide: CdkHeaderCellDef, useExisting: NovoSimpleHeaderCellDef }] }) export class NovoSimpleHeaderCellDef extends _NovoHeaderCellDef { } @Directive({ selector: '[novoSimpleColumnDef]', providers: [{ provide: CdkColumnDef, useExisting: NovoSimpleColumnDef }], }) export class NovoSimpleColumnDef extends _NovoColumnDef { @Input('novoSimpleColumnDef') name: string; } @Directive({ selector: 'novo-simple-header-cell' }) export class NovoSimpleHeaderCell<T> extends _NovoHeaderCell implements OnInit { @HostBinding('attr.role') public role = 'columnheader'; @Input() public column: SimpleTableColumn<T>; constructor(columnDef: CdkColumnDef, private elementRef: ElementRef, private renderer: Renderer2) { super(columnDef, elementRef, renderer); renderer.setAttribute(elementRef.nativeElement, 'data-automation-id', `novo-column-header-${columnDef.cssClassFriendlyName}`); renderer.addClass(elementRef.nativeElement, `novo-column-${columnDef.cssClassFriendlyName}`); renderer.addClass(elementRef.nativeElement, 'novo-simple-header-cell'); } public ngOnInit(): void { if (this.column.width) { this.renderer.setStyle(this.elementRef.nativeElement, 'min-width', `${this.column.width}px`); this.renderer.setStyle(this.elementRef.nativeElement, 'max-width', `${this.column.width}px`); this.renderer.setStyle(this.elementRef.nativeElement, 'width', `${this.column.width}px`); } } } @Directive({ selector: 'novo-simple-empty-header-cell' }) export class NovoSimpleEmptyHeaderCell extends _NovoHeaderCell { @HostBinding('attr.role') public role = 'columnheader'; constructor(columnDef: CdkColumnDef, elementRef: ElementRef, renderer: Renderer2) { super(columnDef, elementRef, renderer); renderer.setAttribute(elementRef.nativeElement, 'data-automation-id', `novo-column-header-${columnDef.cssClassFriendlyName}`); renderer.addClass(elementRef.nativeElement, `novo-column-${columnDef.cssClassFriendlyName}`); renderer.addClass(elementRef.nativeElement, 'novo-simple-empty-header-cell'); } } @Component({ selector: 'novo-simple-checkbox-header-cell', template: `<novo-checkbox [(ngModel)]="selectAll" (ngModelChange)="toggle($event)"></novo-checkbox>` }) export class NovoSimpleCheckboxHeaderCell extends _NovoHeaderCell implements OnDestroy { @HostBinding('attr.role') public role = 'columnheader'; public selectAll: boolean = false; private selectAllSubscription: Subscription; constructor(columnDef: CdkColumnDef, elementRef: ElementRef, renderer: Renderer2, @Optional() private _selection: NovoSelection) { super(columnDef, elementRef, renderer); renderer.setAttribute(elementRef.nativeElement, 'data-automation-id', `novo-checkbox-column-header-${columnDef.cssClassFriendlyName}`); renderer.addClass(elementRef.nativeElement, `novo-checkbox-column-${columnDef.cssClassFriendlyName}`); renderer.addClass(elementRef.nativeElement, 'novo-simple-checkbox-header-cell'); this.selectAllSubscription = _selection.novoSelectAllToggle.subscribe((value: boolean) => { this.selectAll = value; }); } public ngOnDestroy(): void { this.selectAllSubscription.unsubscribe(); } public toggle(value: boolean): void { this._selection.selectAll(value); } } @Component({ selector: 'novo-simple-cell', template: ` <span [class.clickable]="!!column.onClick" (click)="onClick($event)" #span>{{ column.renderer(row) }}</span> `, changeDetection: ChangeDetectionStrategy.OnPush }) export class NovoSimpleCell<T> extends _NovoCell implements OnInit { @HostBinding('attr.role') public role = 'gridcell'; @Input() public row: any; @Input() public column: SimpleTableColumn<T>; @ViewChild('span') private spanElement: ElementRef; constructor(columnDef: CdkColumnDef, private elementRef: ElementRef, private renderer: Renderer2) { super(columnDef, elementRef, renderer); renderer.setAttribute(elementRef.nativeElement, 'data-automation-id', `novo-column-${columnDef.cssClassFriendlyName}`); renderer.addClass(elementRef.nativeElement, `novo-column-${columnDef.cssClassFriendlyName}`); renderer.addClass(elementRef.nativeElement, 'novo-simple-cell'); } public ngOnInit(): void { if (this.column.customClass) { this.renderer.addClass(this.elementRef.nativeElement, this.column.customClass(this.row)); } if (this.column.width) { this.renderer.setStyle(this.elementRef.nativeElement, 'min-width', `${this.column.width}px`); this.renderer.setStyle(this.elementRef.nativeElement, 'max-width', `${this.column.width}px`); this.renderer.setStyle(this.elementRef.nativeElement, 'width', `${this.column.width}px`); // TODO - this inhibits resizing the page after the initial load -- but do we care?!?! // this.renderer.setStyle(this.spanElement.nativeElement, 'min-width', `${this.column.width - 20}px`); // this.renderer.setStyle(this.spanElement.nativeElement, 'max-width', `${this.column.width - 20}px`); // this.renderer.setStyle(this.spanElement.nativeElement, 'width', `${this.column.width - 20}px`); } // else { // // TODO - this inhibits resizing the page after the initial load -- but do we care?!?! // this.renderer.setStyle(this.spanElement.nativeElement, 'min-width', `${this.elementRef.nativeElement.offsetWidth - 20}px`); // this.renderer.setStyle(this.spanElement.nativeElement, 'max-width', `${this.elementRef.nativeElement.offsetWidth - 20}px`); // this.renderer.setStyle(this.spanElement.nativeElement, 'width', `${this.elementRef.nativeElement.offsetWidth - 20}px`); // } } public onClick(event: MouseEvent): void { Helpers.swallowEvent(event); if (this.column.onClick) { this.column.onClick(this.row); } return; } } @Component({ selector: 'novo-simple-checkbox-cell', template: ` <novo-checkbox [ngModel]="selected" (ngModelChange)="toggle($event)"></novo-checkbox> ` }) export class NovoSimpleCheckboxCell extends _NovoCell implements OnDestroy, OnInit { @HostBinding('attr.role') public role = 'gridcell'; @Input() public row: any; @Input() public index: any; public selected: boolean = false; private selectAllSubscription: Subscription; constructor(public columnDef: CdkColumnDef, elementRef: ElementRef, renderer: Renderer2, @Optional() public _selection: NovoSelection) { super(columnDef, elementRef, renderer); renderer.setAttribute(elementRef.nativeElement, 'data-automation-id', `novo-checkbox-column-${columnDef.cssClassFriendlyName}`); renderer.addClass(elementRef.nativeElement, `novo-checkbox-column-${columnDef.cssClassFriendlyName}`); renderer.addClass(elementRef.nativeElement, 'novo-simple-checkbox-cell'); this.selectAllSubscription = _selection.novoSelectAllToggle.subscribe((value: boolean) => { this.selected = value; }); } public ngOnInit(): void { this._selection.register(this.row.id || this.index, this.row); this.selected = this._selection.state.selectedRows.has(this.row.id || this.index); } public ngOnDestroy(): void { this._selection.deregister(this.row.id || this.index); this.selectAllSubscription.unsubscribe(); } public toggle(value: boolean): void { this._selection.toggle(this.row.id || this.index, value, this.row); } } @Component({ selector: 'novo-simple-action-cell', template: ` <ng-container *ngIf="!column.options"> <button theme="icon" [icon]="column.icon" (click)="column.onClick(row)" [disabled]="isDisabled(column, row)"></button> </ng-container> <ng-container *ngIf="column.options"> <novo-dropdown appendToBody="true" parentScrollSelector=".novo-simple-table" containerClass="novo-table-dropdown-cell"> <button type="button" theme="dialogue" icon="collapse" inverse>{{ column.label || labels.actions }}</button> <list> <item *ngFor="let option of column.options" (action)="option.onClick(row)" [disabled]="isDisabled(option, row)"> <span [attr.data-automation-id]="option.label">{{ option.label }}</span> </item> </list> </novo-dropdown> </ng-container> `, changeDetection: ChangeDetectionStrategy.OnPush }) export class NovoSimpleActionCell<T> extends _NovoCell implements OnInit { @HostBinding('attr.role') public role = 'gridcell'; @Input() public row: T; @Input() public column: SimpleTableActionColumn<T>; constructor(columnDef: CdkColumnDef, private elementRef: ElementRef, private renderer: Renderer2, private labels: NovoLabelService) { super(columnDef, elementRef, renderer); renderer.setAttribute(elementRef.nativeElement, 'data-automation-id', `novo-action-column-${columnDef.cssClassFriendlyName}`); } public ngOnInit(): void { if (this.column.options) { this.renderer.addClass(this.elementRef.nativeElement, 'novo-simple-dropdown-cell'); } else { this.renderer.addClass(this.elementRef.nativeElement, 'novo-simple-button-cell'); } } public isDisabled(check: SimpleTableActionColumn<T> | SimpleTableActionColumnOption<T>, row: T): boolean { if (check.disabled === true) { return true; } if (check.disabledCheck) { return check.disabledCheck(row); } return false; } }