diff --git a/packages/dataviews/src/dataviews-layouts/table/index.tsx b/packages/dataviews/src/dataviews-layouts/table/index.tsx
index 7f93a4c14a7ddc..7df010b83b33f2 100644
--- a/packages/dataviews/src/dataviews-layouts/table/index.tsx
+++ b/packages/dataviews/src/dataviews-layouts/table/index.tsx
@@ -43,6 +43,8 @@ interface TableColumnFieldProps< Item > {
item: Item;
isItemClickable: ( item: Item ) => boolean;
onClickItem: ( item: Item ) => void;
+ parent?: string;
+ level?: number;
}
interface TableColumnCombinedProps< Item > {
@@ -59,6 +61,8 @@ interface TableColumnProps< Item > {
primaryField?: NormalizedField< Item >;
fields: NormalizedField< Item >[];
item: Item;
+ parent?: string;
+ level?: number;
column: string;
view: ViewTableType;
isItemClickable: ( item: Item ) => boolean;
@@ -68,6 +72,8 @@ interface TableColumnProps< Item > {
interface TableRowProps< Item > {
hasBulkActions: boolean;
item: Item;
+ parent?: string;
+ level?: number;
actions: Action< Item >[];
fields: NormalizedField< Item >[];
id: string;
@@ -109,6 +115,8 @@ function TableColumn< Item >( {
function TableColumnField< Item >( {
primaryField,
+ parent,
+ level,
item,
field,
isItemClickable,
@@ -131,9 +139,17 @@ function TableColumnField< Item >( {
'dataviews-view-table__primary-field': isPrimaryField,
} ) }
>
+ { isPrimaryField && level !== undefined && (
+
+ { '— '.repeat( level ) }
+
+ ) }
+ { isPrimaryField && parent && (
+ { `Parent: ${ parent }` }
+ ) }
);
}
@@ -159,6 +175,8 @@ function TableColumnCombined< Item >( {
function TableRow< Item >( {
hasBulkActions,
item,
+ parent,
+ level,
actions,
fields,
id,
@@ -247,6 +265,8 @@ function TableRow< Item >( {
onClickItem={ onClickItem }
fields={ fields }
item={ item }
+ parent={ parent }
+ level={ level }
column={ column }
view={ view }
/>
@@ -329,6 +349,118 @@ function ViewTable< Item >( {
( field ) => field.id === view.layout?.primaryField
);
+ const getPrimaryFieldValue = ( item: Item ) => {
+ return primaryField?.getValue( { item } );
+ };
+
+ const getAllDescendants = (
+ index: number,
+ children: { [ key: number ]: Item[] },
+ levels: number[],
+ levelIndex: number
+ ): Item[] => {
+ const nested: Item[] = [];
+
+ if ( children.hasOwnProperty( index ) ) {
+ children[ index ]?.forEach( ( child: Item ) => {
+ const itemId = +getItemId( child );
+ nested.push( child );
+ levels[ itemId ] = levelIndex;
+ nested.push(
+ ...getAllDescendants(
+ itemId,
+ children,
+ levels,
+ levelIndex + 1
+ )
+ );
+ } );
+ }
+
+ return nested;
+ };
+
+ const rows: Item[] = [];
+ const topLevelItemIds: number[] = [];
+ const nestedItemIds: number[] = [];
+ const orphans: number[] = [];
+ const children: { [ key: number ]: Item[] } = {};
+ const primaryFieldValues: string[] = [];
+ const levels: number[] = [];
+ if ( !! view.layout?.hierarchical ) {
+ const hierarchicalField = view.layout?.hierarchical;
+ data.forEach( ( item: Item ) => {
+ const parentId = ( item as Record< string, number > )[
+ hierarchicalField
+ ];
+ const itemId = +getItemId( item );
+ if ( parentId === 0 ) {
+ topLevelItemIds.push( itemId );
+ primaryFieldValues[ itemId ] = getPrimaryFieldValue( item );
+ } else {
+ if ( ! children.hasOwnProperty( parentId ) ) {
+ children[ parentId ] = [];
+ }
+ nestedItemIds.push( itemId );
+ children[ parentId ].push( item );
+ primaryFieldValues[ itemId ] = getPrimaryFieldValue( item );
+ }
+ } );
+ const allIds = [ ...topLevelItemIds, ...nestedItemIds ];
+ Object.keys( children ).forEach( ( key: string ) => {
+ const numericKey = +key;
+ if ( ! allIds.includes( numericKey ) ) {
+ children[ numericKey ].forEach( ( child ) => {
+ orphans.push( +getItemId( child ) );
+ } );
+ }
+ } );
+
+ data.forEach( ( item ) => {
+ const itemId = +getItemId( item );
+ if (
+ topLevelItemIds.includes( itemId ) ||
+ orphans.includes( itemId )
+ ) {
+ rows.push( item );
+ levels[ itemId ] = 0;
+ rows.push(
+ ...getAllDescendants( itemId, children, levels, 1 )
+ );
+ }
+ } );
+ } else {
+ rows.push( ...data );
+ }
+
+ const getItemLevel = ( item: Item ) => {
+ if ( ! view.layout?.hierarchical ) {
+ return;
+ }
+
+ return levels[ +getItemId( item ) ];
+ };
+
+ const getItemParent = ( item: Item ) => {
+ if ( ! view.layout?.hierarchical ) {
+ return;
+ }
+
+ const parent = ( item as Record< string, any > )?.[
+ view.layout?.hierarchical
+ ];
+
+ if ( parent === 0 ) {
+ return;
+ }
+
+ if ( ! primaryFieldValues[ parent ] ) {
+ return;
+ }
+
+ return primaryFieldValues[ parent ];
+ };
+
return (
<>
( {
{ hasData &&
- data.map( ( item, index ) => (
+ rows.map( ( item, index ) => (