@@ -4,7 +4,7 @@ import { WorksheetData } from './worksheet-data';
44
55import { strToU8 } from 'fflate' ;
66import { yieldingLoop } from '../../core/utils' ;
7- import { HeaderType , ExportRecordType , IExportRecord , IColumnList } from '../exporter-common/base-export-service' ;
7+ import { HeaderType , ExportRecordType , IExportRecord , IColumnList , IColumnInfo } from '../exporter-common/base-export-service' ;
88
99/**
1010 * @hidden
@@ -58,13 +58,15 @@ export class ThemeFile implements IExcelFile {
5858export class WorksheetFile implements IExcelFile {
5959 private static MIN_WIDTH = 8.43 ;
6060 private maxOutlineLevel = 0 ;
61+ private sheetData = '' ;
6162 private dimension = '' ;
6263 private freezePane = '' ;
6364 private rowHeight = '' ;
6465
6566 private mergeCellStr = '' ;
6667 private mergeCellsCounter = 0 ;
6768 private rowIndex = 0 ;
69+ private pivotGridRowHeadersMap = new Map < number , string > ( ) ;
6870
6971 public writeElement ( ) { }
7072
@@ -82,74 +84,61 @@ export class WorksheetFile implements IExcelFile {
8284 }
8385
8486 private prepareDataAsync ( worksheetData : WorksheetData , done : ( cols : string , sheetData : string ) => void ) {
85- let sheetData = '' ;
87+ this . sheetData = '' ;
8688 let cols = '' ;
8789 const dictionary = worksheetData . dataDictionary ;
8890 this . rowIndex = 0 ;
8991
9092 if ( worksheetData . isEmpty && ( ! worksheetData . options . alwaysExportHeaders || worksheetData . owner . columns . length === 0 ) ) {
91- sheetData += '<sheetData/>' ;
93+ this . sheetData += '<sheetData/>' ;
9294 this . dimension = 'A1' ;
93- done ( '' , sheetData ) ;
95+ done ( '' , this . sheetData ) ;
9496 } else {
9597 const owner = worksheetData . owner ;
9698 const isHierarchicalGrid = worksheetData . isHierarchical ;
9799 const hasMultiColumnHeader = worksheetData . hasMultiColumnHeader ;
100+ const hasMultiRowHeader = worksheetData . hasMultiRowHeader ;
98101
99102 const hasUserSetIndex = owner . columns . some ( col => col . exportIndex !== undefined ) ;
100103
101104 const height = worksheetData . options . rowHeight ;
102- const rowStyle = isHierarchicalGrid ? ' s="3"' : '' ;
103105 this . rowHeight = height ? ` ht="${ height } " customHeight="1"` : '' ;
106+ this . sheetData += `<sheetData>` ;
104107
105- sheetData += `<sheetData>` ;
108+ let headersForLevel : IColumnInfo [ ] = [ ] ;
109+
110+ for ( let i = 0 ; i <= owner . maxRowLevel ; i ++ ) {
111+ headersForLevel = owner . columns . filter ( c => c . level === i && c . rowSpan > 0 && ! c . skip )
112+
113+ this . printHeaders ( worksheetData , headersForLevel , i , true ) ;
114+
115+ this . rowIndex ++ ;
116+ }
117+
118+ this . rowIndex = 0 ;
106119
107120 for ( let i = 0 ; i <= owner . maxLevel ; i ++ ) {
108121 this . rowIndex ++ ;
109- sheetData += `<row r="${ this . rowIndex } "${ this . rowHeight } >` ;
122+ const pivotGridColumns = this . pivotGridRowHeadersMap . get ( this . rowIndex ) ?? "" ;
123+ this . sheetData += `<row r="${ this . rowIndex } "${ this . rowHeight } >${ pivotGridColumns } ` ;
110124
111- const headersForLevel = hasMultiColumnHeader ?
112- owner . columns
125+ const allowedColumns = owner . columns . filter ( c => c . headerType !== HeaderType . RowHeader && c . headerType !== HeaderType . MultiRowHeader ) ;
126+
127+ headersForLevel = hasMultiColumnHeader ?
128+ allowedColumns
113129 . filter ( c => ( c . level < i &&
114130 c . headerType !== HeaderType . MultiColumnHeader || c . level === i ) && c . columnSpan > 0 && ! c . skip )
115131 . sort ( ( a , b ) => a . startIndex - b . startIndex )
116132 . sort ( ( a , b ) => a . pinnedIndex - b . pinnedIndex ) :
117133 hasUserSetIndex ?
118- owner . columns . filter ( c => ! c . skip ) :
119- owner . columns . filter ( c => ! c . skip )
134+ allowedColumns . filter ( c => ! c . skip ) :
135+ allowedColumns . filter ( c => ! c . skip )
120136 . sort ( ( a , b ) => a . startIndex - b . startIndex )
121137 . sort ( ( a , b ) => a . pinnedIndex - b . pinnedIndex ) ;
122138
123- let startValue = 0 ;
124-
125- for ( const currentCol of headersForLevel ) {
126- if ( currentCol . level === i ) {
127- let columnCoordinate ;
128- columnCoordinate = ExcelStrings . getExcelColumn ( startValue ) + this . rowIndex ;
129- const columnValue = dictionary . saveValue ( currentCol . header , true ) ;
130- sheetData += `<c r="${ columnCoordinate } "${ rowStyle } t="s"><v>${ columnValue } </v></c>` ;
131-
132- if ( i !== owner . maxLevel ) {
133- this . mergeCellsCounter ++ ;
134- this . mergeCellStr += ` <mergeCell ref="${ columnCoordinate } :` ;
135-
136- if ( currentCol . headerType === HeaderType . ColumnHeader ) {
137- columnCoordinate = ExcelStrings . getExcelColumn ( startValue ) + ( owner . maxLevel + 1 ) ;
138- } else {
139- for ( let k = 1 ; k < currentCol . columnSpan ; k ++ ) {
140- columnCoordinate = ExcelStrings . getExcelColumn ( startValue + k ) + this . rowIndex ;
141- sheetData += `<c r="${ columnCoordinate } "${ rowStyle } />` ;
142- }
143- }
144-
145- this . mergeCellStr += `${ columnCoordinate } " />` ;
146- }
147- }
148-
149- startValue += currentCol . columnSpan ;
150- }
139+ this . printHeaders ( worksheetData , headersForLevel , i , false ) ;
151140
152- sheetData += `</row>` ;
141+ this . sheetData += `</row>` ;
153142 }
154143
155144 const multiColumnHeaderLevel = worksheetData . options . ignoreMultiColumnHeaders ? 0 : owner . maxLevel ;
@@ -207,14 +196,14 @@ export class WorksheetFile implements IExcelFile {
207196 }
208197
209198 this . processDataRecordsAsync ( worksheetData , ( rows ) => {
210- sheetData += rows ;
211- sheetData += '</sheetData>' ;
199+ this . sheetData += rows ;
200+ this . sheetData += '</sheetData>' ;
212201
213- if ( hasMultiColumnHeader && this . mergeCellsCounter > 0 ) {
214- sheetData += `<mergeCells count="${ this . mergeCellsCounter } ">${ this . mergeCellStr } </mergeCells>` ;
202+ if ( ( hasMultiColumnHeader || hasMultiRowHeader ) && this . mergeCellsCounter > 0 ) {
203+ this . sheetData += `<mergeCells count="${ this . mergeCellsCounter } ">${ this . mergeCellStr } </mergeCells>` ;
215204 }
216205
217- done ( cols , sheetData ) ;
206+ done ( cols , this . sheetData ) ;
218207 } ) ;
219208 }
220209 }
@@ -237,7 +226,7 @@ export class WorksheetFile implements IExcelFile {
237226 recordHeaders = worksheetData . rootKeys ;
238227 } else {
239228 recordHeaders = worksheetData . owner . columns
240- . filter ( c => c . headerType !== HeaderType . MultiColumnHeader && ! c . skip )
229+ . filter ( c => c . headerType !== HeaderType . MultiColumnHeader && c . headerType !== HeaderType . MultiRowHeader && c . headerType !== HeaderType . RowHeader && ! c . skip )
241230 . sort ( ( a , b ) => a . startIndex - b . startIndex )
242231 . sort ( ( a , b ) => a . pinnedIndex - b . pinnedIndex )
243232 . map ( c => c . field ) ;
@@ -330,13 +319,15 @@ export class WorksheetFile implements IExcelFile {
330319 const sHidden = record . hidden ? ` hidden="1"` : '' ;
331320
332321 this . rowIndex ++ ;
322+ const pivotGridColumns = this . pivotGridRowHeadersMap . get ( this . rowIndex ) ?? "" ;
323+
333324 rowData [ 0 ] =
334- `<row r="${ this . rowIndex } "${ this . rowHeight } ${ outlineLevel } ${ sHidden } >` ;
325+ `<row r="${ this . rowIndex } "${ this . rowHeight } ${ outlineLevel } ${ sHidden } >${ pivotGridColumns } ` ;
335326
336327 const keys = worksheetData . isSpecialData ? [ record . data ] : headersForLevel ;
337328
338329 for ( let j = 0 ; j < keys . length ; j ++ ) {
339- const col = j + ( isHierarchicalGrid ? rowLevel : 0 ) ;
330+ const col = j + ( isHierarchicalGrid ? rowLevel : worksheetData . isPivotGrid ? worksheetData . owner . maxRowLevel : 0 ) ;
340331
341332 const cellData = this . getCellData ( worksheetData , i , col , keys [ j ] ) ;
342333
@@ -381,6 +372,88 @@ export class WorksheetFile implements IExcelFile {
381372 return `<c r="${ columnName } "${ type } ${ format } ><v>${ value } </v></c>` ;
382373 }
383374 }
375+
376+ private printHeaders ( worksheetData : WorksheetData , headersForLevel : IColumnInfo [ ] , i : number , isVertical : boolean ) {
377+ let startValue = 0 ;
378+ let str = '' ;
379+
380+ const isHierarchicalGrid = worksheetData . isHierarchical ;
381+ let rowStyle = isHierarchicalGrid ? ' s="3"' : '' ;
382+ const dictionary = worksheetData . dataDictionary ;
383+ const owner = worksheetData . owner ;
384+ const maxLevel = isVertical
385+ ? owner . maxRowLevel
386+ : owner . maxLevel ;
387+
388+ for ( const currentCol of headersForLevel ) {
389+ const spanLength = isVertical ? currentCol . rowSpan : currentCol . columnSpan ;
390+
391+ if ( currentCol . level === i ) {
392+ let columnCoordinate ;
393+ const column = isVertical
394+ ? this . rowIndex
395+ : startValue + ( owner . maxRowLevel ?? 0 )
396+
397+ const rowCoordinate = isVertical
398+ ? startValue + owner . maxLevel + 2
399+ : this . rowIndex
400+
401+ const columnValue = dictionary . saveValue ( currentCol . header , true ) ;
402+
403+ columnCoordinate = ExcelStrings . getExcelColumn ( column ) + rowCoordinate ;
404+ rowStyle = isVertical && currentCol . rowSpan > 1 ? ' s="4"' : rowStyle ;
405+ str = `<c r="${ columnCoordinate } "${ rowStyle } t="s"><v>${ columnValue } </v></c>` ;
406+
407+ if ( isVertical ) {
408+ if ( this . pivotGridRowHeadersMap . has ( rowCoordinate ) ) {
409+ this . pivotGridRowHeadersMap . set ( rowCoordinate , this . pivotGridRowHeadersMap . get ( rowCoordinate ) + str )
410+ } else {
411+ this . pivotGridRowHeadersMap . set ( rowCoordinate , str )
412+ }
413+ } else {
414+ this . sheetData += str ;
415+ }
416+
417+ if ( i !== maxLevel ) {
418+ this . mergeCellsCounter ++ ;
419+ this . mergeCellStr += ` <mergeCell ref="${ columnCoordinate } :` ;
420+
421+ if ( currentCol . headerType === HeaderType . ColumnHeader ) {
422+ const col = isVertical
423+ ? maxLevel
424+ : startValue + ( owner . maxRowLevel ?? 0 ) ;
425+
426+ const row = isVertical
427+ ? rowCoordinate
428+ : owner . maxLevel + 1 ;
429+
430+ columnCoordinate = ExcelStrings . getExcelColumn ( col ) + row ;
431+ } else {
432+ for ( let k = 1 ; k < spanLength ; k ++ ) {
433+ const col = isVertical
434+ ? column
435+ : column + k ;
436+
437+ const row = isVertical
438+ ? rowCoordinate + k
439+ : this . rowIndex ;
440+
441+ columnCoordinate = ExcelStrings . getExcelColumn ( col ) + row ;
442+ str = `<c r="${ columnCoordinate } "${ rowStyle } />` ;
443+
444+ isVertical
445+ ? this . pivotGridRowHeadersMap . set ( row , str )
446+ : this . sheetData += str
447+ }
448+ }
449+
450+ this . mergeCellStr += `${ columnCoordinate } " />` ;
451+ }
452+ }
453+
454+ startValue += spanLength ;
455+ }
456+ }
384457}
385458
386459/**
0 commit comments