@@ -270,6 +270,9 @@ export class IgxPdfExporterService extends IgxBaseExporter {
270270 // Draw data rows
271271 pdf . setFont ( 'helvetica' , 'normal' ) ;
272272
273+ // Check if this is a tree grid export (tree grids can have both TreeGridRecord and DataRecord types for nested children)
274+ const isTreeGridExport = data . some ( record => record . type === ExportRecordType . TreeGridRecord ) ;
275+
273276 // For pivot grids, get row dimension columns to help with value lookup
274277 const rowDimensionColumnsByLevel : Map < number , any [ ] > = new Map ( ) ;
275278 if ( isPivotGrid && defaultOwner ) {
@@ -333,12 +336,14 @@ export class IgxPdfExporterService extends IgxBaseExporter {
333336 // Calculate indentation for hierarchical records
334337 // TreeGrid supports both hierarchical data and flat self-referencing data (with foreignKey)
335338 // In both cases, the base exporter sets the level property on TreeGridRecord
336- const isTreeGrid = record . type === 'TreeGridRecord' ;
339+ // Note: Nested child records without children are created as DataRecord type,
340+ // but they still have a level property and should be treated as tree grid records
337341 const recordIsHierarchicalGrid = record . type === 'HierarchicalGridRecord' ;
338342
339343 // For tree grids, indentation is visual (in the first column text)
340344 // For hierarchical grids, we don't use indentation (level determines column offset instead)
341- const indentLevel = isTreeGrid ? ( record . level || 0 ) : 0 ;
345+ // If this is a tree grid export and the record has a level property, use it for indentation
346+ const indentLevel = ( isTreeGridExport && record . level !== undefined ) ? ( record . level || 0 ) : 0 ;
342347 const indent = indentLevel * indentSize ;
343348
344349 // Draw parent row
@@ -347,28 +352,21 @@ export class IgxPdfExporterService extends IgxBaseExporter {
347352
348353 // For hierarchical grids, check if this record has child records
349354 if ( recordIsHierarchicalGrid ) {
350- const allDescendants = [ ] ;
355+ const allDescendants : Array < IExportRecord & { __index : number } > = [ ] ;
351356
352- // Collect all descendant records (children, grandchildren, etc.) that belong to this parent
353- // Child records have a different owner (island object) than the parent
354357 let j = i + 1 ;
355358 while ( j < data . length && data [ j ] . level > record . level ) {
356- // Include all descendants (any level deeper)
357359 if ( ! data [ j ] . hidden ) {
358- allDescendants . push ( data [ j ] ) ;
360+ // Attach the original index into data
361+ allDescendants . push ( { ...( data [ j ] as any ) , __index : j } ) ;
359362 }
360363 j ++ ;
361364 }
362365
363- // If there are descendant records, draw child table(s)
364366 if ( allDescendants . length > 0 ) {
365- // Group descendants by owner to separate different child grids
366- // Owner is the actual island object, not a string
367- // Only collect DIRECT children (one level deeper) for initial grouping
368- const directDescendantsByOwner = new Map < any , IExportRecord [ ] > ( ) ;
367+ const directDescendantsByOwner = new Map < any , Array < IExportRecord & { __index : number } > > ( ) ;
369368
370369 for ( const desc of allDescendants ) {
371- // Only include records that are exactly one level deeper (direct children)
372370 if ( desc . level === record . level + 1 ) {
373371 const owner = desc . owner ;
374372 if ( ! directDescendantsByOwner . has ( owner ) ) {
@@ -378,13 +376,12 @@ export class IgxPdfExporterService extends IgxBaseExporter {
378376 }
379377 }
380378
381- // Draw each child grid separately with its direct children only
382379 for ( const [ owner , directChildren ] of directDescendantsByOwner ) {
383380 yPosition = this . drawHierarchicalChildren (
384381 pdf ,
385382 data ,
386- allDescendants , // Pass all descendants so grandchildren can be found
387- directChildren ,
383+ allDescendants , // descendants WITH __index
384+ directChildren , // direct children WITH __index
388385 owner ,
389386 yPosition ,
390387 margin ,
@@ -397,7 +394,6 @@ export class IgxPdfExporterService extends IgxBaseExporter {
397394 ) ;
398395 }
399396
400- // Skip the descendant records we just processed
401397 i = j - 1 ;
402398 }
403399 }
@@ -644,7 +640,7 @@ export class IgxPdfExporterService extends IgxBaseExporter {
644640 private drawHierarchicalChildren (
645641 pdf : jsPDF ,
646642 allData : IExportRecord [ ] ,
647- allDescendants : IExportRecord [ ] , // All descendants to search for grandchildren
643+ allDescendants : any [ ] , // All descendants to search for grandchildren
648644 childRecords : IExportRecord [ ] , // Direct children to render at this level
649645 childOwner : any , // Owner is the island object, not a string
650646 yPosition : number ,
@@ -768,49 +764,67 @@ export class IgxPdfExporterService extends IgxBaseExporter {
768764 this . drawDataRow ( pdf , childRecord , childColumns , [ ] , childTableX , yPosition , childColumnWidth , rowHeight , 0 , options ) ;
769765 yPosition += rowHeight ;
770766
771- // Check if this child has grandchildren (deeper levels in different child grids)
772- // Look for grandchildren in allDescendants that are direct descendants of this childRecord
773- const grandchildrenForThisRecord = allDescendants . filter ( r =>
774- r . level === childRecord . level + 1 && r . type !== 'HeaderRecord'
775- ) ;
767+ // allDescendants here is an array of records with an extra __index property
768+ const childIndex = ( childRecord as any ) . __index as number | undefined ;
769+
770+ if ( childIndex !== undefined ) {
771+ // Find this child's position in allDescendants (by original index)
772+ const childPosInDesc = allDescendants . findIndex ( d => d . __index === childIndex ) ;
776773
777- if ( grandchildrenForThisRecord . length > 0 ) {
778- // Group grandchildren by their owner (different child islands under this record)
779- const grandchildrenByOwner = new Map < any , IExportRecord [ ] > ( ) ;
780-
781- for ( const gc of grandchildrenForThisRecord ) {
782- // Use the actual owner object
783- const gcOwner = gc . owner ;
784- // Only include grandchildren that have a different owner (separate child grid)
785- if ( gcOwner !== childOwner ) {
786- if ( ! grandchildrenByOwner . has ( gcOwner ) ) {
787- grandchildrenByOwner . set ( gcOwner , [ ] ) ;
774+ if ( childPosInDesc !== - 1 ) {
775+ const subtree : Array < IExportRecord & { __index : number } > = [ ] ;
776+ const childLevel = childRecord . level ;
777+
778+ // Collect all deeper records until we hit same-or-higher level
779+ for ( let k = childPosInDesc + 1 ; k < allDescendants . length ; k ++ ) {
780+ const rec = allDescendants [ k ] ;
781+ if ( rec . level <= childLevel ) {
782+ break ;
783+ }
784+ if ( rec . type !== 'HeaderRecord' ) {
785+ subtree . push ( rec ) ;
788786 }
789- grandchildrenByOwner . get ( gcOwner ) ! . push ( gc ) ;
790787 }
791- }
792788
793- // Recursively draw each grandchild owner's records with increased indentation
794- for ( const [ gcOwner , directGrandchildren ] of grandchildrenByOwner ) {
795- yPosition = this . drawHierarchicalChildren (
796- pdf ,
797- allData ,
798- allDescendants , // Pass all descendants so great-grandchildren can be found
799- directGrandchildren , // Direct grandchildren to render
800- gcOwner ,
801- yPosition ,
802- margin ,
803- indentPerLevel + 20 , // Increase indentation for next level
804- usableWidth ,
805- pageHeight ,
806- headerHeight ,
807- rowHeight ,
808- options
809- ) ;
789+ if ( subtree . length > 0 ) {
790+ // Direct grandchildren for this child: exactly one level deeper
791+ const grandchildrenForThisRecord = subtree . filter ( r =>
792+ r . level === childRecord . level + 1 && r . owner !== childOwner
793+ ) ;
794+
795+ if ( grandchildrenForThisRecord . length > 0 ) {
796+ const grandchildrenByOwner = new Map < any , Array < IExportRecord & { __index : number } > > ( ) ;
797+
798+ for ( const gc of grandchildrenForThisRecord ) {
799+ const gcOwner = gc . owner ;
800+ if ( ! grandchildrenByOwner . has ( gcOwner ) ) {
801+ grandchildrenByOwner . set ( gcOwner , [ ] ) ;
802+ }
803+ grandchildrenByOwner . get ( gcOwner ) ! . push ( gc ) ;
804+ }
805+
806+ for ( const [ gcOwner , directGrandchildren ] of grandchildrenByOwner ) {
807+ yPosition = this . drawHierarchicalChildren (
808+ pdf ,
809+ allData ,
810+ subtree , // only this child's subtree for deeper levels
811+ directGrandchildren ,
812+ gcOwner ,
813+ yPosition ,
814+ margin ,
815+ indentPerLevel + 20 ,
816+ usableWidth ,
817+ pageHeight ,
818+ headerHeight ,
819+ rowHeight ,
820+ options
821+ ) ;
822+ }
823+ }
824+ }
810825 }
811826 }
812827 }
813-
814828 // Add spacing after child table
815829 yPosition += 5 ;
816830
0 commit comments