/**
* @file
* @author
* @version
* @since
* @description This is a generic component used to show the cube's outline (dimensions roles tree).
*/
import * as React from 'react';
import Box from '@mui/material/Box';
import { useEffect, useState } from 'react';
import MetaApi from '../utils/meta-api';
const CubeOutline = ({ cubeGid, callback_selected_node }) => {
const [treeData, setTreeData] = useState([]);
const [selectedNodeKey, setSelectedNodeKey] = useState(null);
useEffect(() => {
const initializeData = async () => {
if (cubeGid) {
const dimensionRoles = await MetaApi.load_cube_dim_roles(cubeGid);
const mappedData = dimensionRoles.map((role) => ({
id: `${role.gid}`,
status: '[ + ]',
label: `[Dimension Role] ${role.name}`,
nodeType: 'dimension_role',
entity: role,
subNodes: [],
}));
setTreeData(mappedData);
}
};
initializeData();
}, [cubeGid]);
const updateNodeStatus = async (currentNode) => {
currentNode.status = currentNode.status === '[ + ]' ? '[ - ]' : '[ + ]';
if (currentNode.status === '[ - ]') {
if (currentNode.nodeType === 'dimension_role') {
const role = currentNode.entity;
const hierarchies = await MetaApi.load_dim_hierarchies(role.dimensionGid);
currentNode.subNodes = hierarchies.map((hierarchy) => ({
id: `${role.gid}_${hierarchy.gid}`,
status: '[ + ]',
label: `[Hierarchy Role] ${hierarchy.name}`,
nodeType: 'hierarchy_role',
entity: {
dimensionRole: role,
hierarchy,
},
subNodes: [],
}));
} else if (currentNode.nodeType === 'hierarchy_role') {
const dimRole = currentNode.entity.dimensionRole;
const hierarchy = currentNode.entity.hierarchy;
const members = await MetaApi.load_hierarchy_members(hierarchy.gid);
const rootMember = members.filter((member) => member.parentGid === 0)[0];
currentNode.subNodes = [{
id: `${dimRole.gid}_${rootMember.gid}`,
status: '[ + ]',
label: `[Member Role] ${rootMember.name}`,
nodeType: 'member_role',
entity: {
dimensionRole: dimRole,
member: rootMember,
},
subNodes: [],
}];
} else if (currentNode.nodeType === 'member_role') {
const parentMember = currentNode.entity.member;
const hierarchyId = currentNode.entity.member.hierarchyGid;
let childMembers = await MetaApi.load_hierarchy_members(hierarchyId);
childMembers = childMembers.filter((member) => member.parentGid === parentMember.gid);
currentNode.subNodes = childMembers.map((member) => ({
id: `${currentNode.entity.dimensionRole.gid}_${member.gid}`,
status: '[ + ]',
label: `[Member Role] ${member.name}`,
nodeType: 'member_role',
entity: {
dimensionRole: currentNode.entity.dimensionRole,
member: member,
},
subNodes: [],
}));
}
} else {
currentNode.subNodes = [];
}
setTreeData([...treeData]);
};
const onNodeSelect = (node) => {
setSelectedNodeKey(node.id);
callback_selected_node(node);
};
const renderTreeNode = (node) => {
const isNodeSelected = node.id === selectedNodeKey;
return (
<Box key={node.id} sx={{ paddingLeft: 2 }}>
<Box sx={{ textAlign: 'left' }}>
<Box>
<span
style={{ cursor: 'pointer' }}
onClick={() => updateNodeStatus(node)}
>
{node.status}
</span>
<span
style={{
cursor: 'pointer',
marginLeft: 8,
backgroundColor: isNodeSelected ? '#d3d3d3' : 'transparent',
padding: '2px 4px',
}}
onClick={() => onNodeSelect(node)}
>
{node.label}
</span>
</Box>
</Box>
{node.subNodes && node.subNodes.length > 0 && (
<Box
sx={{
paddingLeft: 2,
overflow: 'hidden',
maxHeight: node.status === '[ - ]' ? '1000px' : '0px',
transition: 'max-height 0.3s ease-in-out',
}}
>
{node.subNodes.map(renderTreeNode)}
</Box>
)}
</Box>
);
};
return (
<Box sx={{ minHeight: 352, minWidth: 250 }}>
{treeData.map(renderTreeNode)}
</Box>
);
};
export default CubeOutline;
React Tree Component with Smooth Expand/Collapse Animation
Posted on Tue, 16 Jun 2026 16:16:17 +0000 by visionmaster