import { MouseEventArgs , Draggable , Droppable , L10n , DropEventArgs , KeyboardEventArgs } from '@syncfusion/ej2-base' ; import { createElement , closest , remove , classList , addClass , removeClass } from '@syncfusion/ej2-base' ; import { isNullOrUndefined , extend } from '@syncfusion/ej2-base' ; import { Column } from '../models/column' ; import { GroupSettingsModel , SortDescriptorModel } from '../base/grid-model' ; import { parentsUntil , isActionPrevent } from '../base/util' ; import { Action , AggregateType } from '../base/enum' ; import { ServiceLocator } from '../services/service-locator' ; import { IGrid , IAction , NotifyArgs } from '../base/interface' ; import * as events from '../base/constant' ; import { AriaService } from '../services/aria-service' ; import { FocusStrategy } from '../services/focus-strategy' ; import { GroupModelGenerator } from '../services/group-model-generator' ; import { DataUtil } from '@syncfusion/ej2-data' ; import { AggregateColumn , AggregateRow } from '../models/aggregate' ; import { Row } from '../models/row' ; import { Cell } from '../models/cell' ; /**
 *
 * The `Group` module is used to handle group action.
 */ export class Group implements IAction { private groupSettings : GroupSettingsModel ; private element : HTMLElement ; private colName : string ; private column : Column ; private isAppliedGroup : boolean = false ; private isAppliedUnGroup : boolean = false ; private groupGenerator : GroupModelGenerator ; private visualElement : HTMLElement = createElement ( 'div' , { className : 'e-cloneproperties e-dragclone e-gdclone' , styles : 'line-height:23px' , attrs : { action : 'grouping' } } ) ; private helper : Function = ( e ) => { let gObj = this . parent ; let target = ( e . sender . target as Element ) ; let element = target . classList . contains ( 'e-groupheadercell' ) ? target as HTMLElement : parentsUntil ( target , 'e-groupheadercell' ) as HTMLElement ; if ( ! element ) { return false ; } this . column = gObj . getColumnByField ( element . firstElementChild . getAttribute ( 'ej-mappingname' ) ) ; this . visualElement . textContent = element . textContent ; this . visualElement . style . width = element . offsetWidth + 2 + 'px' ; this . visualElement . style . height = element . offsetHeight + 2 + 'px' ; this . visualElement . setAttribute ( 'e-mappinguid' , this . column . uid ) ; gObj . element . appendChild ( this . visualElement ) ; return this . visualElement ; } ; private dragStart : Function = ( ) : void => { this . parent . element . classList . add ( 'e-ungroupdrag' ) ; } ; private drag : Function = ( e ) : void => { let target = e . target ; let cloneElement = this . parent . element . querySelector ( '.e-cloneproperties' ) as HTMLElement ; this . parent . trigger ( events . columnDrag , { target : target , draggableType : 'headercell' , column : this . column } ) ; classList ( cloneElement , [ 'e-defaultcur' ] , [ 'e-notallowedcur' ] ) ; if ( ! ( parentsUntil ( target as Element , 'e-gridcontent' ) || parentsUntil ( target as Element , 'e-headercell' ) ) ) { classList ( cloneElement , [ 'e-notallowedcur' ] , [ 'e-defaultcur' ] ) ; } } ; private dragStop : Function = ( e ) => { this . parent . element . classList . remove ( 'e-ungroupdrag' ) ; if ( ! ( parentsUntil ( e . target , 'e-gridcontent' ) || parentsUntil ( e . target , 'e-gridheader' ) ) ) { remove ( e . helper ) ; return ; } } ; private drop : Function = ( e ) => { let gObj = this . parent ; let column = gObj . getColumnByUid ( e . droppedElement . getAttribute ( 'e-mappinguid' ) ) ; this . element . classList . remove ( 'e-hover' ) ; remove ( e . droppedElement ) ; this . aria . setDropTarget ( < HTMLElement > this . parent . element . querySelector ( '.e-groupdroparea' ) , false ) ; this . aria . setGrabbed ( < HTMLElement > this . parent . getHeaderTable ( ) . querySelector ( '[aria-grabbed=true]' ) , false ) ; if ( isNullOrUndefined ( column ) || column . allowGrouping === false || parentsUntil ( gObj . getColumnHeaderByUid ( column . uid ) , 'e-grid' ) . getAttribute ( 'id' ) !== gObj . element . getAttribute ( 'id' ) ) { return ; } this . groupColumn ( column . field ) ; } ; private parent : IGrid ; private serviceLocator : ServiceLocator ; private contentRefresh : boolean = true ; private sortedColumns : string [ ] ; private l10n : L10n ; private aria : AriaService = new AriaService ( ) ; private focus : FocusStrategy ; hidden constructor ( parent ? , groupSettings ? , sortedColumns ? , serviceLocator ? ) { this . parent = parent ; this . groupSettings = groupSettings ; this . serviceLocator = serviceLocator ; this . sortedColumns = sortedColumns ; this . focus = serviceLocator . getService < FocusStrategy > ( 'focus' ) ; this . addEventListener ( ) ; this . groupGenerator = new GroupModelGenerator ( this . parent ) ; } private columnDrag ( e ) { let gObj = this . parent ; let cloneElement = this . parent . element . querySelector ( '.e-cloneproperties' ) as HTMLElement ; classList ( cloneElement , [ 'e-defaultcur' ] , [ 'e-notallowedcur' ] ) ; if ( ! parentsUntil ( e . target as Element , 'e-groupdroparea' ) && ! ( this . parent . allowReordering && parentsUntil ( e . target as Element , 'e-headercell' ) ) ) { classList ( cloneElement , [ 'e-notallowedcur' ] , [ 'e-defaultcur' ] ) ; } e . target . classList . contains ( 'e-groupdroparea' ) ? this . element . classList . add ( 'e-hover' ) : this . element . classList . remove ( 'e-hover' ) ; } private columnDragStart ( e ) { if ( e . target . classList . contains ( 'e-stackedheadercell' ) ) { return ; } let gObj = this . parent ; let dropArea = < HTMLElement > this . parent . element . querySelector ( '.e-groupdroparea' ) ; this . aria . setDropTarget ( dropArea , e . column . allowGrouping ) ; let element = e . target . classList . contains ( 'e-headercell' ) ? e . target : parentsUntil ( e . target as Element , 'e-headercell' ) ; this . aria . setGrabbed ( < HTMLElement > element , true , ! e . column . allowGrouping ) ; } private columnDrop ( e ) { let gObj = this . parent ; if ( e . droppedElement . getAttribute ( 'action' ) === 'grouping' ) { let column = gObj . getColumnByUid ( e . droppedElement . getAttribute ( 'e-mappinguid' ) ) ; if ( isNullOrUndefined ( column ) || column . allowGrouping === false || parentsUntil ( gObj . getColumnHeaderByUid ( column . uid ) , 'e-grid' ) . getAttribute ( 'id' ) !== gObj . element . getAttribute ( 'id' ) ) { return ; } this . ungroupColumn ( column . field ) ; } } hidden public addEventListener ( ) { if ( this . parent . isDestroyed ) { return ; } this . parent . on ( events . uiUpdate , this . enableAfterRender , this ) ; this . parent . on ( events . groupComplete , this . onActionComplete , this ) ; this . parent . on ( events . ungroupComplete , this . onActionComplete , this ) ; this . parent . on ( events . inBoundModelChanged , this . onPropertyChanged , this ) ; this . parent . on ( events . click , this . clickHandler , this ) ; this . parent . on ( events . columnDrag , this . columnDrag , this ) ; this . parent . on ( events . columnDragStart , this . columnDragStart , this ) ; this . parent . on ( events . columnDrop , this . columnDrop , this ) ; this . parent . on ( events . headerRefreshed , this . refreshSortIcons , this ) ; this . parent . on ( events . sortComplete , this . refreshSortIcons , this ) ; this . parent . on ( events . keyPressed , this . keyPressHandler , this ) ; this . parent . on ( events . contentReady , this . initialEnd , this ) ; this . parent . on ( events . onEmpty , this . initialEnd , this ) ; this . parent . on ( events . initialEnd , this . render , this ) ; this . parent . on ( events . groupAggregates , this . onGroupAggregates , this ) ; } hidden public removeEventListener ( ) { if ( this . parent . isDestroyed ) { return ; } this . parent . off ( events . initialEnd , this . render ) ; this . parent . off ( events . uiUpdate , this . enableAfterRender ) ; this . parent . off ( events . groupComplete , this . onActionComplete ) ; this . parent . off ( events . ungroupComplete , this . onActionComplete ) ; this . parent . off ( events . inBoundModelChanged , this . onPropertyChanged ) ; this . parent . off ( events . click , this . clickHandler ) ; this . parent . off ( events . columnDrag , this . columnDrag ) ; this . parent . off ( events . columnDragStart , this . columnDragStart ) ; this . parent . off ( events . columnDrop , this . columnDrop ) ; this . parent . off ( events . headerRefreshed , this . refreshSortIcons ) ; this . parent . off ( events . sortComplete , this . refreshSortIcons ) ; this . parent . off ( events . keyPressed , this . keyPressHandler ) ; this . parent . off ( events . groupAggregates , this . onGroupAggregates ) ; } private initialEnd ( ) { let gObj = this . parent ; this . parent . off ( events . contentReady , this . initialEnd ) ; this . parent . off ( events . onEmpty , this . initialEnd ) ; if ( this . parent . getColumns ( ) . length && this . groupSettings . columns . length ) { this . contentRefresh = false ; for ( let col of gObj . groupSettings . columns ) { this . groupColumn ( col ) ; } this . contentRefresh = true ; } } private keyPressHandler ( e ) { let gObj = this . parent ; if ( ( ! this . groupSettings . columns . length || [ 'altDownArrow' , 'altUpArrow' , 'ctrlDownArrow' , 'ctrlUpArrow' , 'enter' ] . indexOf ( e . action ) === - 1 ) ) { return ; } e . preventDefault ( ) ; switch ( e . action ) { case 'altDownArrow' : case 'altUpArrow' : let selected = gObj . allowSelection ? gObj . getSelectedRowIndexes ( ) : [ ] ; if ( selected . length ) { let rows = gObj . getContentTable ( ) . querySelector ( 'tbody' ) . children ; let dataRow = gObj . getDataRows ( ) [ selected [ selected . length - 1 ] ] as HTMLTableRowElement ; let grpRow ; for ( let i = dataRow . rowIndex ; i >= 0 ; i -- ) { if ( ! rows [ i ] . classList . contains ( 'e-row' ) && ! rows [ i ] . classList . contains ( 'e-detailrow' ) ) { grpRow = rows [ i ] ; break ; } } this . expandCollapseRows ( grpRow . querySelector ( e . action === 'altUpArrow' ? '.e-recordplusexpand' : '.e-recordpluscollapse' ) ) ; } break ; case 'ctrlDownArrow' : this . expandAll ( ) ; break ; case 'ctrlUpArrow' : this . collapseAll ( ) ; break ; case 'enter' : if ( this . parent . isEdit || ( closest ( e . target as Element , '#' + this . parent . element . id + '_searchbar' ) !== null ) ) { return ; } let element = this . focus . getFocusedElement ( ) ; let row = element ? element . parentElement . querySelector ( '[class^="e-record"]' ) : null ; if ( ! row ) { break ; } this . expandCollapseRows ( row ) ; break ; } } private clickHandler ( e ) { this . expandCollapseRows ( e . target as Element ) ; this . applySortFromTarget ( e . target as Element ) ; this . unGroupFromTarget ( e . target as Element ) ; this . toogleGroupFromHeader ( e . target as Element ) ; } private unGroupFromTarget ( target ) { if ( target . classList . contains ( 'e-ungroupbutton' ) ) { this . ungroupColumn ( target . parentElement . getAttribute ( 'ej-mappingname' ) ) ; } } private toogleGroupFromHeader ( target ) { if ( this . groupSettings . showToggleButton ) { if ( target . classList . contains ( 'e-grptogglebtn' ) ) { if ( target . classList . contains ( 'e-toggleungroup' ) ) { this . ungroupColumn ( this . parent . getColumnByUid ( target . parentElement . getAttribute ( 'e-mappinguid' ) ) . field ) ; } else { this . groupColumn ( this . parent . getColumnByUid ( target . parentElement . getAttribute ( 'e-mappinguid' ) ) . field ) ; } } else { if ( target . classList . contains ( 'e-toggleungroup' ) ) { this . ungroupColumn ( target . parentElement . getAttribute ( 'ej-mappingname' ) ) ; } } } } private applySortFromTarget ( target ) { let gObj = this . parent ; let gHeader = closest ( target as Element , '.e-groupheadercell' ) ; if ( gObj . allowSorting && gHeader && ! target . classList . contains ( 'e-ungroupbutton' ) && ! target . classList . contains ( 'e-toggleungroup' ) ) { let field = gHeader . firstElementChild . getAttribute ( 'ej-mappingname' ) ; if ( gObj . getColumnHeaderByField ( field ) . querySelectorAll ( '.e-ascending' ) . length ) { gObj . sortColumn ( field , 'Descending' , true ) ; } else { gObj . sortColumn ( field , 'Ascending' , true ) ; } } } param { Element } target return { void } public expandCollapseRows ( target ) { let trgt = parentsUntil ( target , 'e-recordplusexpand' ) as HTMLTableCellElement || parentsUntil ( target , 'e-recordpluscollapse' ) as HTMLTableCellElement ; if ( trgt ) { let cellIdx = trgt . cellIndex ; let rowIdx = ( trgt . parentElement as HTMLTableRowElement ) . rowIndex ; let rowNodes = this . parent . getContentTable ( ) . querySelector ( 'tbody' ) . children ; let rows = [ ] . slice . call ( rowNodes ) . slice ( rowIdx + 1 , rowNodes . length ) ; let isHide ; let expandElem ; let toExpand = [ ] ; let indent = trgt . parentElement . querySelectorAll ( '.e-indentcell' ) . length ; let expand = false ; if ( trgt . classList . contains ( 'e-recordpluscollapse' ) ) { addClass ( [ trgt ] , 'e-recordplusexpand' ) ; removeClass ( [ trgt ] , 'e-recordpluscollapse' ) ; trgt . firstElementChild . className = 'e-icons e-gdiagonaldown e-icon-gdownarrow' ; expand = true ; } else { isHide = true ; removeClass ( [ trgt ] , 'e-recordplusexpand' ) ; addClass ( [ trgt ] , 'e-recordpluscollapse' ) ; trgt . firstElementChild . className = 'e-icons e-gnextforward e-icon-grightarrow' ; } this . aria . setExpand ( trgt , expand ) ; for ( let i = 0 , len = rows . length ; i < len ; i ++ ) { if ( rows [ i ] . querySelectorAll ( 'td' ) [ cellIdx ] && rows [ i ] . querySelectorAll ( 'td' ) [ cellIdx ] . classList . contains ( 'e-indentcell' ) && rows ) { if ( isHide ) { rows [ i ] . style . display = 'none' ; } else { if ( rows [ i ] . querySelectorAll ( '.e-indentcell' ) . length === indent + 1 ) { rows [ i ] . style . display = '' ; expandElem = rows [ i ] . querySelector ( '.e-recordplusexpand' ) ; if ( expandElem ) { toExpand . push ( expandElem ) ; } if ( rows [ i ] . classList . contains ( 'e-detailrow' ) ) { if ( rows [ i - 1 ] . querySelectorAll ( '.e-detailrowcollapse' ) . length ) { rows [ i ] . style . display = 'none' ; } } } } } else { break ; } } for ( let i = 0 , len = toExpand . length ; i < len ; i ++ ) { removeClass ( [ toExpand [ i ] ] , 'e-recordplusexpand' ) ; addClass ( [ toExpand [ i ] ] , 'e-recordpluscollapse' ) ; toExpand [ i ] . firstElementChild . className = 'e-icons e-gnextforward e-icon-grightarrow' ; this . expandCollapseRows ( toExpand [ i ] ) ; } } } private expandCollapse ( isExpand ) { let rowNodes = this . parent . getContentTable ( ) . querySelector ( 'tbody' ) . children ; let row ; for ( let i = 0 , len = rowNodes . length ; i < len ; i ++ ) { if ( rowNodes [ i ] . querySelectorAll ( '.e-recordplusexpand, .e-recordpluscollapse' ) . length ) { row = rowNodes [ i ] . querySelector ( isExpand ? '.e-recordpluscollapse' : '.e-recordplusexpand' ) ; if ( row ) { row . className = isExpand ? 'e-recordplusexpand' : 'e-recordpluscollapse' ; row . firstElementChild . className = isExpand ? 'e-icons e-gdiagonaldown e-icon-gdownarrow' : 'e-icons e-gnextforward e-icon-grightarrow' ; } if ( ! ( rowNodes [ i ] . firstElementChild . classList . contains ( 'e-recordplusexpand' ) || rowNodes [ i ] . firstElementChild . classList . contains ( 'e-recordpluscollapse' ) ) ) { ( rowNodes [ i ] as HTMLElement ) . style . display = isExpand ? '' : 'none' ; } } else { ( rowNodes [ i ] as HTMLElement ) . style . display = isExpand ? '' : 'none' ; } } } return { void } public expandAll ( ) { this . expandCollapse ( true ) ; } return { void } public collapseAll ( ) { this . expandCollapse ( false ) ; } return { Element } hidden public render ( ) { this . l10n = this . serviceLocator . getService < L10n > ( 'localization' ) ; this . renderGroupDropArea ( ) ; this . initDragAndDrop ( ) ; this . refreshToggleBtn ( ) ; } private renderGroupDropArea ( ) { let groupElem = this . parent . element . querySelector ( '.e-groupdroparea' ) ; if ( groupElem ) { remove ( groupElem ) ; } this . element = this . parent . createElement ( 'div' , { className : 'e-groupdroparea' , attrs : { 'tabindex' : '-1' } } ) ; this . updateGroupDropArea ( ) ; this . parent . element . insertBefore ( this . element , this . parent . element . firstChild ) ; if ( ! this . groupSettings . showDropArea ) { this . element . style . display = 'none' ; } } private updateGroupDropArea ( clear ? ) { if ( this . groupSettings . showDropArea && ! this . groupSettings . columns . length ) { let dragLabel = this . l10n . getConstant ( 'GroupDropArea' ) ; this . element . innerHTML = dragLabel ; this . element . classList . remove ( 'e-grouped' ) ; } else { if ( ( this . element . innerHTML === this . l10n . getConstant ( 'GroupDropArea' ) && ( this . groupSettings . columns . length === 1 || ! this . isAppliedGroup && ! this . isAppliedUnGroup ) ) || clear ) { this . element . innerHTML = '' ; } this . element . classList . add ( 'e-grouped' ) ; } } private initDragAndDrop ( ) { this . initializeGHeaderDrop ( ) ; this . initializeGHeaderDrag ( ) ; } private initializeGHeaderDrag ( ) { let drag = new Draggable ( this . element as HTMLElement , { dragTarget : '.e-groupheadercell' , distance : 5 , helper : this . helper , dragStart : this . dragStart , drag : this . drag , dragStop : this . dragStop } ) ; } private initializeGHeaderDrop ( ) { let gObj = this . parent ; let drop = new Droppable ( this . element , { accept : '.e-dragclone' , drop : this . drop as ( e : DropEventArgs ) => void } ) ; } param { string } columnName return { void } public groupColumn ( columnName ) { let gObj = this . parent ; let column = gObj . getColumnByField ( columnName ) ; if ( isNullOrUndefined ( column ) || column . allowGrouping === false || ( this . contentRefresh && this . groupSettings . columns . indexOf ( columnName ) > - 1 ) ) { return ; } if ( isActionPrevent ( gObj ) ) { gObj . notify ( events . preventBatch , { instance : this , handler : this . groupColumn , arg1 : columnName } ) ; return ; } column . visible = gObj . groupSettings . showGroupedColumn ; this . colName = columnName ; this . isAppliedGroup = true ; if ( this . contentRefresh ) { this . updateModel ( ) ; } else { this . addColToGroupDrop ( columnName ) ; } this . updateGroupDropArea ( ) ; this . isAppliedGroup = false ; } param { string } columnName return { void } public ungroupColumn ( columnName ) { let gObj = this . parent ; let column = this . parent . enableColumnVirtualization ? ( < Column [ ] > this . parent . columns ) . filter ( ( c ) => c . field === columnName ) [ 0 ] : gObj . getColumnByField ( columnName ) ; if ( isNullOrUndefined ( column ) || column . allowGrouping === false || this . groupSettings . columns . indexOf ( columnName ) < 0 ) { return ; } if ( isActionPrevent ( gObj ) ) { gObj . notify ( events . preventBatch , { instance : this , handler : this . ungroupColumn , arg1 : columnName } ) ; return ; } column . visible = true ; this . colName = column . field ; let columns = JSON . parse ( JSON . stringify ( this . groupSettings . columns ) ) ; columns . splice ( columns . indexOf ( this . colName ) , 1 ) ; if ( this . sortedColumns . indexOf ( columnName ) < 0 ) { for ( let i = 0 , len = gObj . sortSettings . columns . length ; i < len ; i ++ ) { if ( columnName === gObj . sortSettings . columns [ i ] . field ) { gObj . sortSettings . columns . splice ( i , 1 ) ; break ; } } } this . groupSettings . columns = columns ; if ( gObj . allowGrouping ) { this . isAppliedUnGroup = true ; this . parent . dataBind ( ) ; } } return { void } hidden public updateModel ( ) { let gObj = this . parent ; let i = 0 ; let columns = JSON . parse ( JSON . stringify ( this . groupSettings . columns ) ) ; columns . push ( this . colName ) ; this . groupSettings . columns = columns ; this . groupAddSortingQuery ( this . colName ) ; this . parent . dataBind ( ) ; } return { void } hidden public onActionComplete ( e ) { let gObj = this . parent ; if ( e . requestType === 'grouping' ) { this . addColToGroupDrop ( this . colName ) ; } else { this . removeColFromGroupDrop ( this . colName ) ; } let args = this . groupSettings . columns . indexOf ( this . colName ) > - 1 ? { columnName : this . colName , requestType : 'grouping' , type : events . actionComplete } : { requestType : 'ungrouping' , type : events . actionComplete } ; this . parent . trigger ( events . actionComplete , extend ( e , args ) ) ; this . colName = null ; } private groupAddSortingQuery ( colName ) { let i = 0 ; while ( i < this . parent . sortSettings . columns . length ) { if ( this . parent . sortSettings . columns [ i ] . field === colName ) { break ; } i ++ ; } if ( this . parent . sortSettings . columns . length === i ) { this . parent . sortSettings . columns . push ( { field : colName , direction : 'Ascending' } ) ; } else if ( ! this . parent . allowSorting ) { this . parent . sortSettings . columns [ i ] . direction = 'Ascending' ; } } private addColToGroupDrop ( field ) { let gObj = this . parent ; let direction = 'Ascending' ; let groupedColumn = this . parent . createElement ( 'div' , { className : 'e-grid-icon e-groupheadercell' } ) ; let childDiv = this . parent . createElement ( 'div' , { attrs : { 'ej-mappingname' : field } } ) ; let column = this . parent . getColumnByField ( field ) ; if ( isNullOrUndefined ( column ) ) { return ; } let headerCell = gObj . getColumnHeaderByUid ( column . uid ) ; if ( ! isNullOrUndefined ( column . headerTemplate ) ) { if ( column . headerTemplate . indexOf ( '#' ) !== - 1 ) { childDiv . innerHTML = document . querySelector ( column . headerTemplate ) . innerHTML . trim ( ) ; } else { childDiv . innerHTML = column . headerTemplate ; } childDiv . firstElementChild . classList . add ( 'e-grouptext' ) ; } else { childDiv . appendChild ( this . parent . createElement ( 'span' , { className : 'e-grouptext' , innerHTML : column . headerText , attrs : { tabindex : '-1' , 'aria-label' : 'sort the grouped column' } } ) ) ; } if ( this . groupSettings . showToggleButton ) { childDiv . appendChild ( this . parent . createElement ( 'span' , { className : 'e-togglegroupbutton e-icons e-icon-ungroup e-toggleungroup' , innerHTML : '&nbsp;' , attrs : { tabindex : '-1' , 'aria-label' : 'ungroup button' } } ) ) ; } if ( headerCell . querySelectorAll ( '.e-ascending,.e-descending' ) . length ) { direction = headerCell . querySelector ( '.e-ascending' ) ? 'Ascending' : 'Descending' ; } childDiv . appendChild ( this . parent . createElement ( 'span' , { className : 'e-groupsort e-icons ' + ( 'e-' + direction . toLowerCase ( ) + ' e-icon-' + direction . toLowerCase ( ) ) , innerHTML : '&nbsp;' , attrs : { tabindex : '-1' , 'aria-label' : 'sort the grouped column' } } ) ) ; childDiv . appendChild ( this . parent . createElement ( 'span' , { className : 'e-ungroupbutton e-icons e-icon-hide' , innerHTML : '&nbsp;' , attrs : { title : this . l10n . getConstant ( 'UnGroup' ) , tabindex : '-1' , 'aria-label' : 'ungroup the grouped column' } , styles : this . groupSettings . showUngroupButton ? '' : 'display:none' } ) ) ; groupedColumn . appendChild ( childDiv ) ; this . element . appendChild ( groupedColumn ) ; } private refreshToggleBtn ( isRemove ? ) { if ( this . groupSettings . showToggleButton ) { let headers = [ ] . slice . call ( this . parent . element . getElementsByClassName ( 'e-headercelldiv' ) ) ; for ( let i = 0 , len = headers . length ; i < len ; i ++ ) { if ( ! ( ( headers [ i ] . classList . contains ( 'e-emptycell' ) ) || ( headers [ i ] . classList . contains ( 'e-headerchkcelldiv' ) ) ) ) { let column = this . parent . getColumnByUid ( headers [ i ] . getAttribute ( 'e-mappinguid' ) ) ; if ( ! this . parent . showColumnMenu || ( this . parent . showColumnMenu && ! column . showColumnMenu ) ) { if ( headers [ i ] . querySelectorAll ( '.e-grptogglebtn' ) . length ) { remove ( headers [ i ] . querySelectorAll ( '.e-grptogglebtn' ) [ 0 ] as Element ) ; } if ( ! isRemove ) { headers [ i ] . appendChild ( this . parent . createElement ( 'span' , { className : 'e-grptogglebtn e-icons ' + ( this . groupSettings . columns . indexOf ( column . field ) > - 1 ? 'e-toggleungroup e-icon-ungroup' : 'e-togglegroup e-icon-group' ) , attrs : { tabindex : '-1' , 'aria-label' : 'Group button' } } ) ) ; } } } } } } private removeColFromGroupDrop ( field ) { if ( ! isNullOrUndefined ( this . getGHeaderCell ( field ) ) ) { remove ( this . getGHeaderCell ( field ) ) ; this . updateGroupDropArea ( ) ; } this . isAppliedUnGroup = false ; } private onPropertyChanged ( e ) { if ( e . module !== this . getModuleName ( ) ) { return ; } for ( let prop of Object . keys ( e . properties ) ) { switch ( prop ) { case 'columns' : let args ; if ( this . contentRefresh ) { if ( ! this . isAppliedUnGroup ) { if ( ! this . isAppliedGroup ) { this . updateGroupDropArea ( true ) ; for ( let i = 0 ; i < this . groupSettings . columns . length ; i ++ ) { this . colName = this . groupSettings . columns [ i ] ; let col = this . parent . getColumnByField ( this . colName ) ; col . visible = this . parent . groupSettings . showGroupedColumn ; this . groupAddSortingQuery ( this . colName ) ; if ( i < this . groupSettings . columns . length - 1 ) { this . addColToGroupDrop ( this . groupSettings . columns [ i ] ) ; } } } args = { columnName : this . colName , requestType : e . properties [ prop ] . length ? 'grouping' : 'ungrouping' , type : events . actionBegin } ; } else { args = { requestType : 'ungrouping' , type : events . actionBegin } ; } if ( ! this . groupSettings . showGroupedColumn ) { e . oldProperties [ prop ] . forEach ( ( column ) => { if ( e . properties [ prop ] . indexOf ( column ) === - 1 ) { this . parent . getColumnByField ( column ) . visible = true ; } } ) ; } this . parent . notify ( events . modelChanged , args ) ; } break ; case 'showDropArea' : this . updateGroupDropArea ( ) ; this . groupSettings . showDropArea ? this . element . style . display = '' : this . element . style . display = 'none' ; break ; case 'showGroupedColumn' : this . updateGroupedColumn ( this . groupSettings . showGroupedColumn ) ; this . parent . notify ( events . modelChanged , { requestType : 'refresh' } ) ; break ; case 'showUngroupButton' : this . updateButtonVisibility ( this . groupSettings . showUngroupButton , 'e-ungroupbutton' ) ; break ; case 'showToggleButton' : this . updateButtonVisibility ( this . groupSettings . showToggleButton , 'e-togglegroupbutton ' ) ; this . parent . refreshHeader ( ) ; break ; } } } private updateGroupedColumn ( isVisible ) { for ( let i = 0 ; i < this . groupSettings . columns . length ; i ++ ) { this . parent . getColumnByField ( this . groupSettings . columns [ i ] ) . visible = isVisible ; } } private updateButtonVisibility ( isVisible , className ) { let gHeader = [ ] . slice . call ( this . element . querySelectorAll ( '.' + className ) ) ; for ( let i = 0 ; i < gHeader . length ; i ++ ) { gHeader [ i ] . style . display = isVisible ? '' : 'none' ; } } private enableAfterRender ( e ) { if ( e . module === this . getModuleName ( ) && e . enable ) { this . render ( ) ; } } return { void } hidden public destroy ( ) { let gridElement = this . parent . element ; if ( ! gridElement || ( ! gridElement . querySelector ( '.e-gridheader' ) && ! gridElement . querySelector ( '.e-gridcontent' ) ) ) { return ; } if ( ! this . parent . isDestroyed ) { this . clearGrouping ( ) ; } this . removeEventListener ( ) ; this . refreshToggleBtn ( true ) ; remove ( this . element ) ; } return { void } public clearGrouping ( ) { let cols = JSON . parse ( JSON . stringify ( this . groupSettings . columns ) ) ; this . contentRefresh = false ; for ( let i = 0 , len = cols . length ; i < len ; i ++ ) { this . ungroupColumn ( cols [ i ] ) ; } this . contentRefresh = true ; } private protected getModuleName ( ) { return 'group' ; } private refreshSortIcons ( e ? ) { let gObj = this . parent ; let header ; let cols = gObj . sortSettings . columns ; let gCols = gObj . groupSettings . columns ; let fieldNames = this . parent . getColumns ( ) . map ( ( c ) => c . field ) ; this . refreshToggleBtn ( ) ; for ( let i = 0 , len = cols . length ; i < len ; i ++ ) { if ( fieldNames . indexOf ( cols [ i ] . field ) === - 1 ) { continue ; } header = gObj . getColumnHeaderByField ( cols [ i ] . field ) ; if ( ! gObj . allowSorting && ( this . sortedColumns . indexOf ( cols [ i ] . field ) > - 1 || this . groupSettings . columns . indexOf ( cols [ i ] . field ) > - 1 ) ) { classList ( header . querySelector ( '.e-sortfilterdiv' ) , [ 'e-ascending' , 'e-icon-ascending' ] , [ ] ) ; if ( cols . length > 1 ) { header . querySelector ( '.e-headercelldiv' ) . appendChild ( this . parent . createElement ( 'span' , { className : 'e-sortnumber' , innerHTML : ( i + 1 ) . toString ( ) } ) ) ; } } else if ( this . getGHeaderCell ( cols [ i ] . field ) && this . getGHeaderCell ( cols [ i ] . field ) . querySelectorAll ( '.e-groupsort' ) . length ) { if ( cols [ i ] . direction === 'Ascending' ) { classList ( this . getGHeaderCell ( cols [ i ] . field ) . querySelector ( '.e-groupsort' ) , [ 'e-ascending' , 'e-icon-ascending' ] , [ 'e-descending' , 'e-icon-descending' ] ) ; } else { classList ( this . getGHeaderCell ( cols [ i ] . field ) . querySelector ( '.e-groupsort' ) , [ 'e-descending' , 'e-icon-descending' ] , [ 'e-ascending' , 'e-icon-ascending' ] ) ; } } } for ( let i = 0 , len = gCols . length ; i < len ; i ++ ) { if ( fieldNames . indexOf ( gCols [ i ] ) === - 1 ) { continue ; } gObj . getColumnHeaderByField ( gCols [ i ] ) . setAttribute ( 'aria-grouped' , 'true' ) ; } } private getGHeaderCell ( field ) { if ( this . element && this . element . querySelector ( '[ej-mappingname="' + field + '"]' ) ) { return this . element . querySelector ( '[ej-mappingname="' + field + '"]' ) . parentElement ; } return null ; } private onGroupAggregates ( editedData ) { let aggregates = this . iterateGroupAggregates ( editedData ) ; let rowData = this . groupGenerator . generateRows ( aggregates , { } ) ; let summaryRows = this . parent . getRowsObject ( ) . filter ( ( row ) => ! row . isDataRow ) ; let updateSummaryRows = rowData . filter ( ( data ) => ! data . isDataRow ) ; updateSummaryRows . forEach ( ( row , indx ) => { let cells = row . cells . filter ( ( cell ) => cell . isDataCell ) ; let args = { cells : cells , data : row . data , dataUid : summaryRows [ indx ] ? summaryRows [ indx ] . uid : '' } ; this . parent . notify ( events . refreshAggregateCell , args ) ; } ) ; } private iterateGroupAggregates ( editedData ) { let updatedData = editedData instanceof Array ? editedData : [ ] ; let rows = this . parent . getRowsObject ( ) ; let initData = this . parent . getCurrentViewRecords ( ) ; let deletedCols = [ ] ; let changeds = rows . map ( ( row ) => { if ( row . edit === 'delete' ) { deletedCols . push ( row . data ) ; } return row . changes instanceof Object ? row . changes : row . data ; } ) ; let field = this . parent . getPrimaryKeyFieldNames ( ) [ 0 ] ; changeds = updatedData . length === 0 ? changeds : updatedData ; let mergeData = initData . map ( ( item ) => { let pKeyVal = DataUtil . getObject ( field , item ) ; let value ; let hasVal = changeds . some ( ( cItem ) => { value = cItem ; return pKeyVal === DataUtil . getObject ( field , cItem ) ; } ) ; return hasVal ? value : item ; } ) ; let eData = editedData ; if ( ! ( ( < { type : string ; } > eData ) . type && ( < { type : string ; } > eData ) . type === 'cancel' ) && deletedCols . length > 0 ) { deletedCols . forEach ( ( row ) => { let index = mergeData . indexOf ( row ) ; mergeData . splice ( index , 1 ) ; } ) ; } let aggregates = [ ] ; let aggregateRows = this . parent . aggregates ; aggregateRows . forEach ( ( row ) => { row . columns . forEach ( ( col ) => { let aggr = { } ; let type = col . type . toString ( ) ; aggr = { type : type . toLowerCase ( ) , field : col . field } ; aggregates . push ( aggr ) ; } ) ; } ) ; let result ; let aggrds ; let groupedCols = this . parent . groupSettings . columns ; groupedCols . forEach ( ( field ) => { aggrds = result ? result : mergeData ; result = DataUtil . group ( aggrds , field , aggregates , null , null ) ; } ) ; return result ; } }