import { Browser } from '@syncfusion/ej2-base' ; import { Group } from '@syncfusion/ej2-data' ; import { IModelGenerator , IGrid , VirtualInfo , NotifyArgs } from '../base/interface' ; import { Row } from '../models/row' ; import { Column } from '../models/column' ; import { PageSettingsModel } from '../models/page-settings-model' ; import { RowModelGenerator } from '../services/row-model-generator' ; import { GroupModelGenerator } from '../services/group-model-generator' ; /**
 * Content module is used to render grid content
 */ export class VirtualRowModelGenerator implements IModelGenerator < Column > { private model : PageSettingsModel ; public rowModelGenerator : IModelGenerator < Column > ; public parent : IGrid ; public cOffsets : { [ x ] : number ; } = { } ; public cache : { [ x ] : Row < Column > [ ] ; } = { } ; public data : { [ x ] : Object [ ] ; } = { } ; public groups : { [ x ] : Object ; } = { } ; constructor ( parent ) { this . parent = parent ; this . model = this . parent . pageSettings ; this . rowModelGenerator = this . parent . allowGrouping ? new GroupModelGenerator ( this . parent ) : new RowModelGenerator ( this . parent ) ; } public generateRows ( data , notifyArgs ? ) { let info = notifyArgs . virtualInfo = notifyArgs . virtualInfo || this . getData ( ) ; let xAxis = info . sentinelInfo && info . sentinelInfo . axis === 'X' ; let page = ! xAxis && info . loadNext && ! info . loadSelf ? info . nextInfo . page : info . page ; let result = [ ] ; let center = ~ ~ ( this . model . pageSize / 2 ) ; let indexes = this . getBlockIndexes ( page ) ; let loadedBlocks = [ ] ; this . checkAndResetCache ( notifyArgs . requestType ) ; if ( this . parent . enableColumnVirtualization ) { info . blockIndexes . forEach ( ( value ) => { if ( this . isBlockAvailable ( value ) ) { this . cache [ value ] = this . rowModelGenerator . refreshRows ( this . cache [ value ] ) ; } } ) ; } info . blockIndexes . forEach ( ( value ) => { if ( ! this . isBlockAvailable ( value ) ) { let rows = this . rowModelGenerator . generateRows ( data , { virtualInfo : info , startIndex : this . getStartIndex ( value , data ) } ) ; let median = ~ ~ Math . max ( rows . length , this . model . pageSize ) / 2 ; if ( ! this . isBlockAvailable ( indexes [ 0 ] ) ) { this . cache [ indexes [ 0 ] ] = rows . slice ( 0 , median ) ; } if ( ! this . isBlockAvailable ( indexes [ 1 ] ) ) { this . cache [ indexes [ 1 ] ] = rows . slice ( median ) ; } } if ( this . parent . groupSettings . columns . length && ! xAxis && this . cache [ value ] ) { this . cache [ value ] = this . updateGroupRow ( this . cache [ value ] , value ) ; } result . push ( ... this . cache [ value ] ) ; if ( this . isBlockAvailable ( value ) ) { loadedBlocks . push ( value ) ; } } ) ; info . blockIndexes = loadedBlocks ; let grouping = 'records' ; if ( this . parent . allowGrouping ) { this . parent . currentViewData [ grouping ] = result . map ( ( m ) => m . data ) ; } else { this . parent . currentViewData = result . map ( ( m ) => m . data ) ; } return result ; } public getBlockIndexes ( page ) { return [ page + ( page - 1 ) , page * 2 ] ; } public getPage ( block ) { return block % 2 === 0 ? block / 2 : ( block + 1 ) / 2 ; } public isBlockAvailable ( value ) { return value in this . cache ; } public getData ( ) { return { page : this . model . currentPage , blockIndexes : this . getBlockIndexes ( this . model . currentPage ) , direction : 'down' , columnIndexes : this . parent . getColumnIndexesInView ( ) } ; } private getStartIndex ( blk , data , full = true ) { let page = this . getPage ( blk ) ; let even = blk % 2 === 0 ; let index = ( page - 1 ) * this . model . pageSize ; return full || ! even ? index : index + ~ ~ ( this . model . pageSize / 2 ) ; } public getColumnIndexes ( content = ( < HTMLElement > this . parent . getHeaderContent ( ) . firstChild ) ) { let indexes = [ ] ; let sLeft = content . scrollLeft | 0 ; let keys = Object . keys ( this . cOffsets ) ; let cWidth = content . getBoundingClientRect ( ) . width ; sLeft = Math . min ( this . cOffsets [ keys . length - 1 ] - cWidth , sLeft ) ; let calWidth = Browser . isDevice ? 2 * cWidth : cWidth / 2 ; let left = sLeft + cWidth + ( sLeft === 0 ? calWidth : 0 ) ; keys . some ( ( offset , indx , input ) => { let iOffset = Number ( offset ) ; let offsetVal = this . cOffsets [ offset ] ; let border = sLeft - calWidth <= offsetVal && left + calWidth >= offsetVal ; if ( border ) { indexes . push ( iOffset ) ; } return left + calWidth < offsetVal ; } ) ; return indexes ; } public checkAndResetCache ( action ) { let clear = [ 'paging' , 'refresh' , 'sorting' , 'filtering' , 'searching' , 'grouping' , 'ungrouping' , 'reorder' ] . some ( ( value ) => action === value ) ; if ( clear ) { this . cache = { } ; this . data = { } ; this . groups = { } ; } return clear ; } public refreshColOffsets ( ) { let col = 0 ; this . cOffsets = { } ; let gLen = this . parent . groupSettings . columns . length ; let cols = ( < Column [ ] > this . parent . columns ) ; let cLen = cols . length ; let isVisible = ( column ) => column . visible && ( ! this . parent . groupSettings . showGroupedColumn ? this . parent . groupSettings . columns . indexOf ( column . field ) < 0 : column . visible ) ; this . parent . groupSettings . columns . forEach ( ( c , n ) => this . cOffsets [ n ] = ( this . cOffsets [ n - 1 ] | 0 ) + 30 ) ; Array . apply ( null , Array ( cLen ) ) . map ( ( ) => col ++ ) . forEach ( ( block , i ) => { block = block + gLen ; this . cOffsets [ block ] = ( this . cOffsets [ block - 1 ] | 0 ) + ( isVisible ( cols [ i ] ) ? parseInt ( < string > cols [ i ] . width , 10 ) : 0 ) ; } ) ; } public updateGroupRow ( current , block ) { let currentFirst = current [ 0 ] ; let rows = [ ] ; Object . keys ( this . cache ) . forEach ( ( key ) => { if ( Number ( key ) < block ) { rows = [ ... rows , ... this . cache [ key ] ] ; } } ) ; if ( ( currentFirst && currentFirst . isDataRow ) || block % 2 === 0 ) { return current ; } return this . iterateGroup ( current , rows ) ; } private iterateGroup ( current , rows ) { let currentFirst = current [ 0 ] ; let offset = 0 ; if ( currentFirst && currentFirst . isDataRow ) { return current ; } let isPresent = current . some ( ( row ) => { return rows . some ( ( oRow , index ) => { let res = oRow && ( < Group > oRow . data ) . field !== undefined && ( < Group > oRow . data ) . field === ( < Group > row . data ) . field && ( < Group > oRow . data ) . key === ( < Group > row . data ) . key ; if ( res ) { offset = index ; } return res ; } ) ; } ) ; if ( isPresent ) { current . shift ( ) ; current = this . iterateGroup ( current , rows . slice ( offset ) ) ; } return current ; } public getRows ( ) { let rows = [ ] ; Object . keys ( this . cache ) . forEach ( ( key ) => rows = [ ... rows , ... this . cache [ key ] ] ) ; return rows ; } }