@@ -151,40 +151,57 @@ export class IgxPdfExporterService extends IgxBaseExporter {
151151
152152 // For hierarchical grids, check if this record has child records
153153 if ( isHierarchicalGrid ) {
154- const childRecords = [ ] ;
155- let childOwner = null ;
154+ const allDescendants = [ ] ;
156155
157- // Collect only direct child records (next level) that belong to this parent
156+ // Collect all descendant records (children, grandchildren, etc.) that belong to this parent
157+ // Child records have a different owner (island object) than the parent
158158 let j = i + 1 ;
159- while ( j < data . length && data [ j ] . owner !== DEFAULT_OWNER && data [ j ] . level > record . level ) {
160- // Only include direct children (one level deeper)
161- if ( data [ j ] . level === record . level + 1 && ! data [ j ] . hidden ) {
162- childRecords . push ( data [ j ] ) ;
163- if ( ! childOwner ) {
164- childOwner = data [ j ] . owner ;
165- }
159+ while ( j < data . length && data [ j ] . level > record . level ) {
160+ // Include all descendants (any level deeper)
161+ if ( ! data [ j ] . hidden ) {
162+ allDescendants . push ( data [ j ] ) ;
166163 }
167164 j ++ ;
168165 }
169166
170- // If there are child records, draw a child table
171- if ( childRecords . length > 0 && childOwner ) {
172- yPosition = this . drawHierarchicalChildren (
173- pdf ,
174- data ,
175- childRecords ,
176- childOwner ,
177- yPosition ,
178- margin ,
179- childTableIndent ,
180- usableWidth ,
181- pageHeight ,
182- headerHeight ,
183- rowHeight ,
184- options
185- ) ;
167+ // If there are descendant records, draw child table(s)
168+ if ( allDescendants . length > 0 ) {
169+ // Group descendants by owner to separate different child grids
170+ // Owner is the actual island object, not a string
171+ // Only collect DIRECT children (one level deeper) for initial grouping
172+ const directDescendantsByOwner = new Map < any , IExportRecord [ ] > ( ) ;
173+
174+ for ( const desc of allDescendants ) {
175+ // Only include records that are exactly one level deeper (direct children)
176+ if ( desc . level === record . level + 1 ) {
177+ const owner = desc . owner ;
178+ if ( ! directDescendantsByOwner . has ( owner ) ) {
179+ directDescendantsByOwner . set ( owner , [ ] ) ;
180+ }
181+ directDescendantsByOwner . get ( owner ) ! . push ( desc ) ;
182+ }
183+ }
184+
185+ // Draw each child grid separately with its direct children only
186+ for ( const [ owner , directChildren ] of directDescendantsByOwner ) {
187+ yPosition = this . drawHierarchicalChildren (
188+ pdf ,
189+ data ,
190+ allDescendants , // Pass all descendants so grandchildren can be found
191+ directChildren ,
192+ owner ,
193+ yPosition ,
194+ margin ,
195+ childTableIndent ,
196+ usableWidth ,
197+ pageHeight ,
198+ headerHeight ,
199+ rowHeight ,
200+ options
201+ ) ;
202+ }
186203
187- // Skip the child records we just processed
204+ // Skip the descendant records we just processed
188205 i = j - 1 ;
189206 }
190207 }
@@ -281,8 +298,9 @@ export class IgxPdfExporterService extends IgxBaseExporter {
281298 private drawHierarchicalChildren (
282299 pdf : jsPDF ,
283300 allData : IExportRecord [ ] ,
284- childRecords : IExportRecord [ ] ,
285- childOwner : string ,
301+ allDescendants : IExportRecord [ ] , // All descendants to search for grandchildren
302+ childRecords : IExportRecord [ ] , // Direct children to render at this level
303+ childOwner : any , // Owner is the island object, not a string
286304 yPosition : number ,
287305 margin : number ,
288306 indentPerLevel : number ,
@@ -292,6 +310,7 @@ export class IgxPdfExporterService extends IgxBaseExporter {
292310 rowHeight : number ,
293311 options : IgxPdfExporterOptions
294312 ) : number {
313+ // Get columns for this child owner (owner is the island object)
295314 const childColumns = this . _ownersMap . get ( childOwner ) ?. columns . filter (
296315 col => col . field && ! col . skip && col . headerType === ExportHeaderType . ColumnHeader
297316 ) || [ ] ;
@@ -300,74 +319,81 @@ export class IgxPdfExporterService extends IgxBaseExporter {
300319 return yPosition ;
301320 }
302321
303- // Add some spacing before child table
304- yPosition += 5 ;
322+ // Filter out header records - they should not be rendered as data rows
323+ const dataRecords = childRecords . filter ( r => r . type !== 'HeaderRecord' ) ;
305324
306- // Check if child table fits on current page
307- const childTableHeight = headerHeight + ( childRecords . length * rowHeight ) + 10 ;
308- if ( yPosition + childTableHeight > pageHeight - margin ) {
309- pdf . addPage ( ) ;
310- yPosition = margin ;
325+ if ( dataRecords . length === 0 ) {
326+ return yPosition ;
311327 }
312328
329+ // Add some spacing before child table
330+ yPosition += 5 ;
331+
313332 // Draw child table with indentation
314333 const childTableWidth = usableWidth - indentPerLevel ;
315334 const childColumnWidth = childTableWidth / childColumns . length ;
316335 const childTableX = margin + indentPerLevel ;
317336
337+ // Check if we need a new page for headers
338+ if ( yPosition + headerHeight > pageHeight - margin ) {
339+ pdf . addPage ( ) ;
340+ yPosition = margin ;
341+ }
342+
318343 // Draw child table headers
319344 this . drawTableHeaders ( pdf , childColumns , childTableX , yPosition , childColumnWidth , headerHeight , childTableWidth , options ) ;
320345 yPosition += headerHeight ;
321346
322- // Process each child record
323- let childIndex = 0 ;
324- while ( childIndex < childRecords . length ) {
325- const childRecord = childRecords [ childIndex ] ;
326-
327- // Check if this child record has its own children (next level in hierarchy)
328- const childRecordIndex = allData . indexOf ( childRecord ) ;
329- const grandchildrenByOwner = new Map < string , IExportRecord [ ] > ( ) ;
330-
331- if ( childRecordIndex >= 0 && childRecordIndex + 1 < allData . length ) {
332- // Look for grandchildren and group them by owner (each owner represents a different child island)
333- let k = childRecordIndex + 1 ;
334-
335- // Collect all grandchildren that belong to this child, grouped by owner
336- while ( k < allData . length && allData [ k ] . level > childRecord . level ) {
337- // Only include direct children (next level)
338- if ( allData [ k ] . level === childRecord . level + 1 && ! allData [ k ] . hidden ) {
339- const owner = allData [ k ] . owner . toString ( ) ;
340- if ( ! grandchildrenByOwner . has ( owner ) ) {
341- grandchildrenByOwner . set ( owner , [ ] ) ;
342- }
343- grandchildrenByOwner . get ( owner ) ! . push ( allData [ k ] ) ;
344- }
345- k ++ ;
346- }
347+ // Find the minimum level in these records (direct children of parent)
348+ const minLevel = Math . min ( ...dataRecords . map ( r => r . level ) ) ;
349+
350+ // Process each record at the minimum level (direct children)
351+ const directChildren = dataRecords . filter ( r => r . level === minLevel ) ;
352+
353+ for ( const childRecord of directChildren ) {
354+ // Check if we need a new page
355+ if ( yPosition + rowHeight > pageHeight - margin ) {
356+ pdf . addPage ( ) ;
357+ yPosition = margin ;
358+ // Redraw headers on new page
359+ this . drawTableHeaders ( pdf , childColumns , childTableX , yPosition , childColumnWidth , headerHeight , childTableWidth , options ) ;
360+ yPosition += headerHeight ;
347361 }
348362
349- // If this child has grandchildren, render them as nested child tables (one per owner/island)
350- if ( grandchildrenByOwner . size > 0 ) {
351- // Check if we need a new page for parent row
352- if ( yPosition + rowHeight > pageHeight - margin ) {
353- pdf . addPage ( ) ;
354- yPosition = margin ;
355- // Redraw headers on new page
356- this . drawTableHeaders ( pdf , childColumns , childTableX , yPosition , childColumnWidth , headerHeight , childTableWidth , options ) ;
357- yPosition += headerHeight ;
358- }
363+ // Draw the child record
364+ this . drawDataRow ( pdf , childRecord , childColumns , childTableX , yPosition , childColumnWidth , rowHeight , 0 , options ) ;
365+ yPosition += rowHeight ;
359366
360- // Draw the parent row (child record)
361- this . drawDataRow ( pdf , childRecord , childColumns , childTableX , yPosition , childColumnWidth , rowHeight , 0 , options ) ;
362- yPosition += rowHeight ;
367+ // Check if this child has grandchildren (deeper levels in different child grids)
368+ // Look for grandchildren in allDescendants that are direct descendants of this childRecord
369+ const grandchildrenForThisRecord = allDescendants . filter ( r =>
370+ r . level === childRecord . level + 1 && r . type !== 'HeaderRecord'
371+ ) ;
372+
373+ if ( grandchildrenForThisRecord . length > 0 ) {
374+ // Group grandchildren by their owner (different child islands under this record)
375+ const grandchildrenByOwner = new Map < any , IExportRecord [ ] > ( ) ;
376+
377+ for ( const gc of grandchildrenForThisRecord ) {
378+ // Use the actual owner object
379+ const gcOwner = gc . owner ;
380+ // Only include grandchildren that have a different owner (separate child grid)
381+ if ( gcOwner !== childOwner ) {
382+ if ( ! grandchildrenByOwner . has ( gcOwner ) ) {
383+ grandchildrenByOwner . set ( gcOwner , [ ] ) ;
384+ }
385+ grandchildrenByOwner . get ( gcOwner ) ! . push ( gc ) ;
386+ }
387+ }
363388
364- // Recursively draw each island 's grandchildren as a separate nested child table
365- for ( const [ grandchildOwner , grandchildRecords ] of grandchildrenByOwner ) {
389+ // Recursively draw each grandchild owner 's records with increased indentation
390+ for ( const [ gcOwner , directGrandchildren ] of grandchildrenByOwner ) {
366391 yPosition = this . drawHierarchicalChildren (
367392 pdf ,
368393 allData ,
369- grandchildRecords ,
370- grandchildOwner ,
394+ allDescendants , // Pass all descendants so great-grandchildren can be found
395+ directGrandchildren , // Direct grandchildren to render
396+ gcOwner ,
371397 yPosition ,
372398 margin ,
373399 indentPerLevel + 30 , // Increase indentation for next level
@@ -378,22 +404,7 @@ export class IgxPdfExporterService extends IgxBaseExporter {
378404 options
379405 ) ;
380406 }
381- } else {
382- // No grandchildren, just draw this child as a regular row
383- // Check if we need a new page
384- if ( yPosition + rowHeight > pageHeight - margin ) {
385- pdf . addPage ( ) ;
386- yPosition = margin ;
387- // Redraw headers on new page
388- this . drawTableHeaders ( pdf , childColumns , childTableX , yPosition , childColumnWidth , headerHeight , childTableWidth , options ) ;
389- yPosition += headerHeight ;
390- }
391-
392- this . drawDataRow ( pdf , childRecord , childColumns , childTableX , yPosition , childColumnWidth , rowHeight , 0 , options ) ;
393- yPosition += rowHeight ;
394407 }
395-
396- childIndex ++ ;
397408 }
398409
399410 // Add spacing after child table
0 commit comments