import { Component , OnInit , Input , Output , EventEmitter , ElementRef , AfterViewInit , ViewChild , ChangeDetectionStrategy , OnChanges , DoCheck } from '@angular/core' ; import { GanttService } from '../shared/services/gantt.service' ; import { GanttConfig } from '../shared/services/gantt-config.service' ; import { IGanttOptions , Zooming } from '../shared/interfaces' ; @ Component ( { selector : 'gantt-activity' , template : `
    <div class="actions_bar">
        <div style="float:right">
            <label>
                <button (click)="zoomTasks('hours')" style="background-color:whitesmoke; border:none; font-size:16px; cursor:pointer">Hour</button>
            </label>
            <label>
                <button (click)="zoomTasks('days')" style="background-color:whitesmoke; border:none; font-size:16px; cursor:pointer">Day</button>
            </label>
            <button (click)="expand()" style="background-color:whitesmoke; border:none; font-size:21px; cursor:pointer" [innerHTML]="activityActions.expandedIcon"></button>
        </div>
    </div>
    <div class="grid" #ganttGrid [ngStyle]="{ 'height': ganttActivityHeight, 'width': ganttService.gridWidth + 'px'}">
    <div class="grid_scale" [ngStyle]="setGridScaleStyle()">
        <div class="grid_head_cell" *ngFor="let column of gridColumns" [style.width]="column.width + 'px'" [style.left]="column.left + 'px'">
            <label>{{column.name}} <span *ngIf="column.name === 'Duration'" style="font-weight:bold">{{ ganttService.calculateTotalDuration(ganttService.TASK_CACHE) }}</span></label>            
        </div>
        <div class="grid_head_cell">
            <button (click)="toggleAllChildren()" style="background-color:whitesmoke; border:none; font-size:21px; cursor:pointer">{{ treeExpanded ? '&#x25b2;' : '&#x25bc;' }}</button>
        </div>
    </div>
    <div class="grid_data" #ganttGridData [ngStyle]="{ 'height': ganttService.calculateGanttHeight() }">
    <div #row *ngFor="let data of ganttService.groupData(ganttService.TASK_CACHE)" (click)="toggleChildren(row, data)" class="grid_row" [ngStyle]="setGridRowStyle(ganttService.isParent(data.treePath))" [attr.data-id]="ganttService.setIdPrefix(data.id)"  [attr.data-isParent]="ganttService.isParent(data.treePath)" [attr.data-parentid]="ganttService.setIdPrefix(data.parentId)">
            <div class="grid_cell" [ngStyle]="{ 'width': gridColumns[0].width + 'px' }">
                <div [innerHTML]="getStatusIcon(data.status, data.percentComplete)" [style.color]="getStatusIconColor(data.status, data.percentComplete)"></div>
            </div>
            <div class="grid_cell" [ngStyle]=" { 'width': gridColumns[1].width + 'px', 'padding-left': ganttService.isChild(data.treePath) }">
                <div class="gantt_tree_content">{{data.name}}</div>                
            </div>
            <div class="grid_cell" [ngStyle]="{ 'width': gridColumns[2].width + 'px' }">
                <!--<div>{{ ganttService.isParent(data.treePath) === true ? ganttService.calculateParentTotalPercentage(data, project.tasks) : data.percentComplete }}</div>-->
                <div>{{ data.percentComplete }}</div>
            </div>
            <div class="grid_cell" [ngStyle]="{ 'width': gridColumns[3].width + 'px'}">
                <div> {{ ganttService.calculateDuration(data) }}</div>
            </div>
        </div>
    </div>
    </div><div class="gantt_activity" (window:resize)="onResize($event)" [ngStyle]="{ 'height': ganttActivityHeight, 'width': ganttActivityWidth - 18 + 'px'}">
        <time-scale [zoom]="zoom" [zoomLevel]="zoomLevel" [timeScale]="ganttService.TIME_SCALE" [dimensions]="dimensions"></time-scale>
        <div class="gantt_activity_area" #ganttActivityArea [ngStyle]="{ 'height': ganttService.calculateGanttHeight(), 'width': containerWidth + 'px'}">
            <activity-background [zoom]="zoom" [zoomLevel]="zoomLevel" [timeScale]="ganttService.TIME_SCALE" [tasks]="ganttService.TASK_CACHE"></activity-background>
            <activity-bars [zoom]="zoom" [zoomLevel]="zoomLevel" [timeScale]="ganttService.TIME_SCALE" [dimensions]="dimensions" [tasks]="ganttService.TASK_CACHE"></activity-bars>
        </div>
    </div>
    <div class="gantt_vertical_scroll" #verticalScroll (scroll)="onVerticalScroll(verticalScroll, ganttGrid, ganttActivityArea)" [ngStyle]="{'display': activityActions.expanded === true ? 'none' : 'block' }">
        <div [ngStyle]="{ 'height': ganttService.calculateGanttHeight() }"></div>
    </div>
    ` , styles : [ `
        .gantt_activity {
            /*overflow-x: hidden;*/
            overflow-x: auto;
            height: 250px;
            overflow-y: hidden;
            overflow-x: scroll;
            display: inline-block;
            vertical-align: top;
            position:relative;
        }

        .gantt_activity_area {
            position: relative;
            overflow-x: hidden;
            overflow-y: hidden;
            -webkit-user-select: none;
        }

        .gantt_vertical_scroll {
            background-color: transparent;
            overflow-x: hidden;
            overflow-y: scroll;
            position: absolute;
            right: 0;
            display: block;
            height: 283px;
            width: 18px;
            top: 70px;
        }

        .grid {
            overflow-x: hidden;
            overflow-y: hidden;
            display: inline-block;
            vertical-align: top;
            border-right: 1px solid #cecece;
        }

        .grid_scale {
            color: #6b6b6b;
            font-size: 12px;
            border-bottom: 1px solid #e0e0e0;
            background-color: whitesmoke;
        }

        .grid_head_cell {
            /*color: #a6a6a6;*/
            border-top: none !important;
            border-right: none !important;
            line-height: inherit;
            box-sizing: border-box;
            display: inline-block;
            vertical-align: top;
            border-right: 1px solid #cecece;
            /*text-align: center;*/
            position: relative;
            cursor: default;
            height: 100%;
            -moz-user-select: -moz-none;
            -webkit-user-select: none;
            overflow: hidden;
        }

        .grid_data {
            overflow:hidden;   
        }

        .grid_row {
            box-sizing: border-box;
            border-bottom: 1px solid #e0e0e0;
            background-color: #fff;
            position: relative;
            -webkit-user-select: none;
        }

        .grid_row:hover {
            background-color: #eeeeee;
        }

        .grid_cell {
            border-right: none;
            color: #454545;
            display: inline-block;
            vertical-align: top;
            padding-left: 6px;
            padding-right: 6px;
            height: 100%;
            overflow: hidden;
            white-space: nowrap;
            font-size: 13px;
            box-sizing: border-box;
        }

        .actions_bar {
            /*border-top: 1px solid #cecece;*/
            border-bottom: 1px solid #e0e0e0;
            clear: both;
            /*margin-top: 90px;*/
            height: 28px;
            background: whitesmoke;
            color: #494949;
            font-family: Arial, sans-serif;
            font-size: 13px;
            padding-left: 15px;
            line-height: 25px;
        }

        .gantt_tree_content {
            padding-left:15px;
        }
    ` ] , changeDetection : ChangeDetectionStrategy . Default } ) export class GanttActivityComponent implements OnInit , DoCheck { @ Input ( ) project : any ; @ Input ( ) options : any ; @ Output ( ) onGridRowClick : EventEmitter < any > = new EventEmitter < any > ( ) ; private upTriangle : string = '&#x25b2;' ; private downTriangle : string = '&#x25bc;' ; private zoom : EventEmitter < string > = new EventEmitter < string > ( ) ; private activityActions = { expanded : false , expandedIcon : this . downTriangle } ; private timeScale : any ; private start : Date ; private end : Date ; private containerHeight : any ; private containerWidth : any ; private activityContainerSizes : any ; private ganttActivityHeight : any ; private ganttActivityWidth : any ; private zoomLevel : string = Zooming [ Zooming . hours ] ; private treeExpanded = false ; private scale : any = { start : null , end : null } ; private dimensions = { height : 0 , width : 0 } ; private data : any [ ] = [ ] ; public gridColumns : any [ ] = [ { name : '' , left : 0 , width : 16 } , { name : 'Task' , left : 20 , width : 330 } , { name : '%' , left : 8 , width : 40 } , { name : 'Duration' , left : 14 , width : 140 } ] ; constructor ( public elem , private ganttService ) { } ngOnInit ( ) { this . ganttService . TASK_CACHE = this . project . tasks . slice ( 0 ) . filter ( ( item ) => { return item . treePath . split ( '/' ) . length === 1 ; } ) ; this . ganttService . TIME_SCALE = this . ganttService . calculateScale ( this . options . scale . start , this . options . scale . end ) ; this . zoomLevel = this . options . zooming ; this . start = this . options . scale . start ; this . end = this . options . scale . end ; this . containerWidth = this . calculateContainerWidth ( ) ; this . containerHeight = this . calculateContainerHeight ( ) ; this . activityContainerSizes = this . ganttService . calculateActivityContainerDimensions ( ) ; this . setScale ( ) ; this . setDimensions ( ) ; this . setSizes ( ) ; this . expand ( ) ; } /** Custom model check */ ngDoCheck ( ) { var tasksAdded = this . ganttService . doTaskCheck ( this . project . tasks , this . treeExpanded ) ; if ( tasksAdded && this . activityActions . expanded ) { this . expand ( true ) ; } } /** On vertical scroll set the scroll top of grid and activity  */ onVerticalScroll ( verticalScroll , ganttGrid , ganttActivityArea ) { this . ganttService . scrollTop ( verticalScroll , ganttGrid , ganttActivityArea ) ; } /** Removes or adds children for given parent tasks back into DOM by updating TASK_CACHE */ toggleChildren ( rowElem , task ) { try { let isParent = "true" === rowElem . getAttribute ( 'data-isparent' ) ; let parentId = rowElem . getAttribute ( 'data-parentid' ) . replace ( "_" , "" ) ; let children = document . querySelectorAll ( '[data-parentid=' + rowElem . getAttribute ( 'data-parentid' ) + '][data-isparent=false]' ) ; if ( isParent ) { if ( children . length > 0 ) { let childrenIds = this . ganttService . TASK_CACHE . filter ( ( task ) => { return task . parentId == parentId && task . treePath . split ( '/' ) . length > 1 ; } ) . map ( ( item ) => { return item . id ; } ) ; childrenIds . forEach ( ( item ) => { var removedIndex = this . ganttService . TASK_CACHE . map ( ( item ) => { return item . id ; } ) . indexOf ( item ) ; this . ganttService . TASK_CACHE . splice ( removedIndex , 1 ) ; } ) ; if ( this . activityActions . expanded ) { this . expand ( true ) ; } } else { let childrenTasks = this . project . tasks . filter ( ( task ) => { return task . parentId === parentId && task . treePath . split ( '/' ) . length > 1 ; } ) ; childrenTasks . forEach ( ( task ) => { this . ganttService . TASK_CACHE . push ( task ) ; } ) ; if ( this . activityActions . expanded ) { this . expand ( true ) ; } } } this . onGridRowClick . emit ( task ) ; } catch ( err ) { } } /** Removes or adds children tasks back into DOM by updating TASK_CACHE */ toggleAllChildren ( ) { try { var children = document . querySelectorAll ( '[data-isparent=false]' ) ; var childrenIds = Array . prototype . slice . call ( children ) . map ( ( item ) => { return item . getAttribute ( 'data-id' ) . replace ( "_" , "" ) ; } ) ; if ( this . treeExpanded ) { if ( children . length > 0 ) { let childrenIds = this . ganttService . TASK_CACHE . filter ( ( task ) => { return task . treePath . split ( '/' ) . length > 1 ; } ) . map ( ( item ) => { return item . id ; } ) ; childrenIds . forEach ( ( item ) => { var removedIndex = this . ganttService . TASK_CACHE . map ( ( item ) => { return item . id ; } ) . indexOf ( item ) ; this . ganttService . TASK_CACHE . splice ( removedIndex , 1 ) ; } ) ; } this . treeExpanded = false ; if ( this . activityActions . expanded ) { this . expand ( true ) ; } } else { let childrenTasks = this . project . tasks . filter ( ( task ) => { return task . treePath . split ( '/' ) . length > 1 ; } ) ; if ( children . length > 0 ) { childrenTasks = childrenTasks . filter ( ( task ) => { return childrenIds . indexOf ( task . id ) === - 1 ; } ) ; } childrenTasks . forEach ( ( task ) => { this . ganttService . TASK_CACHE . push ( task ) ; } ) ; this . treeExpanded = true ; if ( this . activityActions . expanded ) { this . expand ( true ) ; } } } catch ( err ) { } } /** On resize of browser window dynamically adjust gantt activity height and width */ onResize ( event ) { let activityContainerSizes = this . ganttService . calculateActivityContainerDimensions ( ) ; if ( this . activityActions . expanded ) { this . ganttActivityHeight = this . ganttService . TASK_CACHE . length * this . ganttService . rowHeight + this . ganttService . rowHeight * 3 + 'px' ; } else { this . ganttActivityHeight = activityContainerSizes . height + 'px' ; ; } this . ganttActivityWidth = activityContainerSizes . width ; } setScale ( ) { this . scale . start = this . start ; this . scale . end = this . end ; } setDimensions ( ) { this . dimensions . height = this . containerHeight ; this . dimensions . width = this . containerWidth ; } setGridRowStyle ( isParent ) { if ( isParent ) { return { 'height' : this . ganttService . rowHeight + 'px' , 'line-height' : this . ganttService . rowHeight + 'px' , 'font-weight' : 'bold' , 'cursor' : 'pointer' } ; } return { 'height' : this . ganttService . rowHeight + 'px' , 'line-height' : this . ganttService . rowHeight + 'px' } ; } /** Set the zoom level e.g hours, days */ zoomTasks ( level ) { this . zoomLevel = level ; this . zoom . emit ( this . zoomLevel ) ; this . containerWidth = this . calculateContainerWidth ( ) ; this . setDimensions ( ) ; document . querySelector ( '.gantt_activity' ) . scrollLeft = 0 ; } /** Expand the gantt grid and activity area height */ expand ( force ? ) { var verticalScroll = document . querySelector ( '.gantt_vertical_scroll' ) ; var ganttActivityHeight = `${ this . ganttService . TASK_CACHE . length * this . ganttService . rowHeight + this . ganttService . rowHeight * 3 }px` ; if ( force && this . activityActions . expanded ) { this . ganttActivityHeight = ganttActivityHeight ; } else if ( this . activityActions . expanded ) { this . activityActions . expanded = false ; this . activityActions . expandedIcon = this . downTriangle ; this . ganttActivityHeight = '300px' ; } else { verticalScroll . scrollTop = 0 ; this . activityActions . expanded = true ; this . activityActions . expandedIcon = this . upTriangle ; this . ganttActivityHeight = ganttActivityHeight ; } } /** Get the status icon unicode string */ getStatusIcon ( status , percentComplete ) { var checkMarkIcon = '&#x2714;' ; var upBlackPointer = '&#x25b2;' ; var crossMarkIcon = '&#x2718;' ; if ( status === "Completed" || percentComplete === 100 && status !== "Error" ) { return checkMarkIcon ; } else if ( status === "Warning" ) { return upBlackPointer ; } else if ( status === "Error" ) { return crossMarkIcon ; } return '' ; } /** Get the status icon color */ getStatusIconColor ( status , percentComplete ) { if ( status === "Completed" || percentComplete === 100 && status !== "Error" ) { return 'green' ; } else if ( status === "Warning" ) { return 'orange' ; } else if ( status === "Error" ) { return 'red' ; } return '' ; } private setGridScaleStyle ( ) { var height = this . ganttService . rowHeight ; if ( this . zoomLevel === Zooming [ Zooming . hours ] ) { height *= 2 ; } return { 'height' : height + 'px' , 'line-height' : height + 'px' , 'width' : this . ganttService . gridWidth + 'px' } ; } private calculateContainerHeight ( ) { return this . ganttService . TASK_CACHE . length * this . ganttService . rowHeight ; } private calculateContainerWidth ( ) { if ( this . zoomLevel === Zooming [ Zooming . hours ] ) { return this . ganttService . TIME_SCALE . length * this . ganttService . hourCellWidth * 24 + this . ganttService . hourCellWidth ; } else { return this . ganttService . TIME_SCALE . length * this . ganttService . cellWidth + this . ganttService . cellWidth ; } } private setSizes ( ) { this . ganttActivityHeight = this . activityContainerSizes . height + 'px' ; this . ganttActivityWidth = this . activityContainerSizes . width ; } }