import { ChangeDetectionStrategy , Component , ElementRef , EventEmitter , Input , isDevMode , OnDestroy , OnInit , Output , TemplateRef , ViewChild , ViewEncapsulation } from '@angular/core' ; import { Observable , ReplaySubject , merge } from 'rxjs' ; import { Column , GroupRowsBy , Sort , SortDirection , TableState } from './table.types' ; import { PageLengthEntry } from './components/table-page-size/table-page-size.component' ; import { takeUntil } from 'rxjs/operators' ; import { TableService } from './table.service' ; import { Router } from '@angular/router' ; import { FormGroup } from '@angular/forms' ; @ Component ( { selector : 'cmdb-table' , templateUrl : './table.component.html' , styleUrls : [ './table.component.scss' ] , encapsulation : ViewEncapsulation . None , changeDetection : ChangeDetectionStrategy . Default , } ) export class TableComponent < T > implements OnInit , OnDestroy { /**
     * `ViewChild` for accessing the complete table container.
     */ @ ViewChild ( 'container' , { static : true } ) public containerRef : ElementRef ; /**
     * `ViewChild` for accessing the table html element.
     */ @ ViewChild ( 'table' , { static : true } ) public tableRef : ElementRef ; /**
     * Array of possible custom buttons displayed in header.
     */ @ Input ( ) public customButtons : Array < TemplateRef < any > > ; private private subscriber : ReplaySubject < void > = new ReplaySubject < void > ( ) ; /**
     * html table id
     */ @ Input ( ) public id : string = `table-${ Math . random ( ) . toString ( 36 ) . substring ( 10 ) }` ; /**
     * Message which will be shown if no data are in the table.
     */ @ Input ( ) public emptyMessage : string = 'No data to display!' ; /**
     * Component wide debounce time.
     */ @ Input ( ) public debounceTime : number = 500 ; /**
     * Is the table data currently in loading.
     */ @ Input ( ) public loading : boolean = false ; /**
     * Should the loading spinner be loaded.
     */ @ Input ( ) public loadingEnabled : boolean = true ; /**
     * Columns
     */ public columns : Array < Column > = [ ] ; /**
     * Column holder for reset.
     */ @ Input ( ) public initialVisibleColumns : Array < string > = [ ] ; param columns @ Input ( 'columns' ) public set Columns ( columns ) { this . columns = columns ; this . items = [ ] ; } /**
     * Column search form group.
     */ public columnSearchForm : FormGroup ; /**
     * Column search columnSearchForm group.
     */ public columnSearchIconHidden : boolean = false ; /**
     * Column search input enabled.
     */ @ Input ( ) public columnSearchEnabled : boolean = false ; /**
     * Event Emitter for column search input change.
     */ @ Output ( ) public columnSearchChange : EventEmitter < any [ ] > = new EventEmitter < any [ ] > ( ) ; /**
     * Items or data which will be inserted into the tbody.
     */ @ Input ( ) public items : Array < T > = [ ] ; /**
     * Total number of items which exists for this table.
     */ @ Input ( ) public totalItems : number = 0 ; /**
     * Is row selected enabled.
     */ @ Input ( ) public selectEnabled : boolean = false ; /**
     * Selected items.
     */ @ Input ( ) public selectedItems : Array < T > = [ ] ; /**
     * Event emitter when selected items changed.
     */ @ Output ( ) public selectedChange : EventEmitter < Array < T > > = new EventEmitter < Array < T > > ( ) ; /**
     * Initial page site
     */ public initPage : number = 1 ; @ Input ( 'initPage' ) public set InitPage ( page ) { this . initPage = page ; this . page = page ; } /**
     * Current displayed page number.
     */ public page : number = this . initPage ; @ Input ( 'page' ) public set Page ( page ) { this . page = page ; } /**
     * Pagination enabled.
     */ @ Input ( ) public paginationEnabled : boolean = true ; /**
     * Event Emitter when the current page was changed.
     */ @ Output ( ) public pageChange : EventEmitter < number > = new EventEmitter < number > ( ) ; /**
     * Is a switching of the page size possible.
     */ @ Input ( ) public pageSizeEnabled : boolean = true ; /**
     * Current page size.
     */ @ Input ( ) public pageSize : number = 10 ; /**
     * Possible page size list.
     */ @ Input ( ) public pageSizeList : Array < PageLengthEntry > ; /**
     * Event Emitter when the page size changed.
     */ @ Output ( ) public pageSizeChange : EventEmitter < number > = new EventEmitter < number > ( ) ; /**
     * Search input enabled.
     */ @ Input ( ) public searchEnabled : boolean = true ; /**
     * Event Emitter for search input change.
     */ @ Output ( ) public searchChange : EventEmitter < string > = new EventEmitter < string > ( ) ; /**
     * Info display enabled.
     */ @ Input ( ) public infoEnabled : boolean = true ; /**
     * Row group parameter.
     */ @ Input ( ) public groupRowsBy : GroupRowsBy ; /**
     * Are columns toggleable.
     */ @ Input ( ) public toggleable : boolean = false ; /**
     * Dynamic list of css classes for the table body.
     */ @ Input ( ) public bodyClasses : Array < string > = [ ] ; /**
     * Dynamic list of css classes for column heads.
     */ @ Input ( ) public columnClasses : Array < string > = [ ] ; /**
     * Dynamic list of css classes for rows.
     */ @ Input ( ) public rowClasses : Array < string > = [ ] ; /**
     * Visibility change emitter.
     */ @ Output ( ) public columnVisibilityChange : EventEmitter < Column > = new EventEmitter < Column > ( ) ; /**
     * Sorting enabled.
     */ @ Input ( ) public sortable : boolean = true ; /**
     * Sort change emitter.
     */ @ Output ( ) public sortChange : EventEmitter < Sort > = new EventEmitter < Sort > ( ) ; /**
     * Table sort value.
     */ public sort : Sort = { name : null , order : SortDirection . NONE } ; param value @ Input ( 'sort' ) public set Sort ( value ) { this . sort = value ; } /**
     * Event emitter when any config was changed.
     */ @ Input ( ) public stateEnabled : boolean = false ; /**
     * List of possible table configs.
     */ @ Input ( ) public tableStates : Array < TableState > = [ ] ; /**
     * Current user table config.
     */ @ Input ( ) public tableState : TableState ; /**
     * Event emitter when any config was changed.
     */ @ Output ( ) public stateChange : EventEmitter < TableState > = new EventEmitter < TableState > ( ) ; /**
     * Config save emit.
     */ @ Output ( ) public stateSelect : EventEmitter < TableState > = new EventEmitter < TableState > ( ) ; @ Output ( ) public stateSave : EventEmitter < TableState > = new EventEmitter < TableState > ( ) ; @ Output ( ) public stateUpdate : EventEmitter < TableState > = new EventEmitter < TableState > ( ) ; @ Output ( ) public stateDelete : EventEmitter < TableState > = new EventEmitter < TableState > ( ) ; @ Output ( ) public stateReset : EventEmitter < void > = new EventEmitter < void > ( ) ; public constructor ( private tableService , private router ) { } public ngOnInit ( ) { this . configChangeObservable . pipe ( takeUntil ( this . subscriber ) ) . subscribe ( ( ) => { this . tableState = { name , page : this . page , pageSize : this . pageSize , sort : this . sort , visibleColumns : this . columns . filter ( c => ! c . hidden ) . map ( c => c . name ) } as TableState ; if ( this . stateEnabled ) { this . tableService . setCurrentTableState ( this . router . url , this . id , this . tableState ) ; this . stateChange . emit ( this . tableState ) ; } } ) ; if ( isDevMode ( ) ) { this . pageSizeChange . asObservable ( ) . pipe ( takeUntil ( this . subscriber ) ) . subscribe ( ( size ) => { console . log ( `[TableEvent] Page size changed to: ${ size }` ) ; } ) ; this . pageChange . asObservable ( ) . pipe ( takeUntil ( this . subscriber ) ) . subscribe ( ( page ) => { console . log ( `[TableEvent] Page changed to: ${ page }` ) ; } ) ; this . searchChange . asObservable ( ) . pipe ( takeUntil ( this . subscriber ) ) . subscribe ( ( search ) => { console . log ( `[TableEvent] Search input changed to: ${ search }` ) ; } ) ; this . selectedChange . asObservable ( ) . pipe ( takeUntil ( this . subscriber ) ) . subscribe ( ( selected ) => { console . log ( `[TableEvent] Selected rows changed to` ) ; console . log ( selected ) ; } ) ; this . sortChange . asObservable ( ) . pipe ( takeUntil ( this . subscriber ) ) . subscribe ( ( sort ) => { console . log ( `[TableEvent] Sort changed to:` ) ; console . log ( sort ) ; } ) ; } } private private get configChangeObservable ( ) : Observable < any > { return merge ( this . pageChange . asObservable ( ) , this . columnVisibilityChange . asObservable ( ) , this . sortChange . asObservable ( ) , this . pageSizeChange . asObservable ( ) ) ; } param item param event public toggleRowSelection ( item , event ) { const checked = event . currentTarget . checked ; if ( checked ) { this . selectedItems . push ( item ) ; } else { const idx = this . selectedItems . indexOf ( item ) ; if ( idx !== - 1 ) { this . selectedItems . splice ( idx , 1 ) ; } } this . selectedChange . emit ( this . selectedItems ) ; } param event public toggleAllRowSelections ( event ) { const checked = event . currentTarget . checked ; if ( checked ) { this . selectedItems = [ ... this . items ] ; } else { this . selectedItems = [ ] ; } this . selectedChange . emit ( this . selectedItems ) ; } param column public onColumnVisibilityChange ( column ) { const col = this . columns . find ( c => c . name === column . name ) ; const colIndex = this . columns . indexOf ( col ) ; this . columns [ colIndex ] . hidden = column . hidden ; this . columnVisibilityChange . emit ( column ) ; } /**
     * Reset the columns to the initial set.
     */ public onColumnsReset ( ) { for ( const col of this . columns ) { col . hidden = ! this . initialVisibleColumns . includes ( col . name ) ; } this . columnVisibilityChange . emit ( ) ; } /**
     * On hidden change of a column search.
     */ public onColumnSearchVisibilityChange ( ) { this . columnSearchIconHidden = ! this . columnSearchIconHidden ; this . columnSearchChange . emit ( [ ] ) ; } param sort public onSortChange ( sort ) { this . sort = sort ; this . sortChange . emit ( sort ) ; } param page public onPageChange ( page ) { this . selectedItems = [ ] ; this . page = page ; this . pageChange . emit ( page ) ; } param name public onStateSave ( name ) { const columns = this . columns . filter ( c => ! c . hidden ) . map ( c => c . name ) ; const tableState = { name , page : this . page , pageSize : this . pageSize , sort : this . sort , visibleColumns : columns } as TableState ; this . tableState = tableState ; this . tableStates . push ( tableState ) ; this . tableService . addTableState ( this . router . url , this . id , tableState ) ; this . stateSave . emit ( tableState ) ; } param state public onStateUpdate ( state ) { const columns = this . columns . filter ( c => ! c . hidden ) . map ( c => c . name ) ; const tableState = { name : state . name , page : this . page , pageSize : this . pageSize , sort : this . sort , visibleColumns : columns } as TableState ; this . tableState = tableState ; const stateIDX = this . tableStates . indexOf ( state ) ; if ( stateIDX > - 1 ) { this . tableStates [ stateIDX ] = this . tableState ; } this . tableService . updateTableState ( this . router . url , this . id , state , this . tableState ) ; this . stateUpdate . emit ( tableState ) ; } param state public onStateSelect ( state ) { this . tableState = state ; this . tableService . setCurrentTableState ( this . router . url , this . id , state ) ; this . stateSelect . emit ( state ) ; } param state public onStateDelete ( state ) { this . tableService . removeTableState ( this . router . url , this . id , state ) ; this . stateDelete . emit ( state ) ; } /**
     * Emit current state reset.
     */ public onStateReset ( ) { this . tableService . setCurrentTableState ( this . router . url , this . id , undefined ) ; this . stateReset . emit ( ) ; } public ngOnDestroy ( ) { this . subscriber . next ( ) ; this . subscriber . complete ( ) ; } }