Make PgTreeView react component more customisable

This commit is contained in:
Kundan 2024-07-31 14:07:47 +05:30 committed by GitHub
parent d39fe78239
commit c45fb47b08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -13,7 +13,7 @@ import CheckBoxIcon from '@mui/icons-material/CheckBox';
const Root = styled('div')(({theme}) => ({ const Root = styled('div')(({ theme }) => ({
height: '100%', height: '100%',
'& .PgTree-tree': { '& .PgTree-tree': {
background: theme.palette.background.default, background: theme.palette.background.default,
@ -38,9 +38,11 @@ const Root = styled('div')(({theme}) => ({
export const PgTreeSelectionContext = React.createContext(); export const PgTreeSelectionContext = React.createContext();
export default function PgTreeView({ data = [], hasCheckbox = false, selectionChange = null}) { export default function PgTreeView({ data = [], hasCheckbox = false,
selectionChange = null, NodeComponent = null, ...props }) {
let treeData = data; let treeData = data;
const Node = NodeComponent ?? DefaultNode;
const treeObj = useRef(); const treeObj = useRef();
const treeContainerRef = useRef(); const treeContainerRef = useRef();
const [selectedCheckBoxNodes, setSelectedCheckBoxNodes] = React.useState([]); const [selectedCheckBoxNodes, setSelectedCheckBoxNodes] = React.useState([]);
@ -51,9 +53,9 @@ export default function PgTreeView({ data = [], hasCheckbox = false, selectionCh
let selectedChildNodes = []; let selectedChildNodes = [];
treeObj.current.selectedNodes.forEach((node) => { treeObj.current.selectedNodes.forEach((node) => {
if(node.isInternal && !node.isOpen) { if (node.isInternal && !node.isOpen) {
node.children.forEach((ch)=>{ node.children.forEach((ch) => {
if(ch.data.isSelected && ch.isLeaf && !selectedChildNodes.includes(ch.id)) { if (ch.data.isSelected && ch.isLeaf && !selectedChildNodes.includes(ch.id)) {
selectedChildNodes.push(ch.id); selectedChildNodes.push(ch.id);
selectedChNodes.push(ch); selectedChNodes.push(ch);
} }
@ -68,7 +70,7 @@ export default function PgTreeView({ data = [], hasCheckbox = false, selectionCh
}; };
return (<Root> return (<Root>
{ treeData.length > 0 ? {treeData.length > 0 ?
<PgTreeSelectionContext.Provider value={selectedCheckBoxNodes}> <PgTreeSelectionContext.Provider value={selectedCheckBoxNodes}>
<div ref={(containerRef) => treeContainerRef.current = containerRef} className={'PgTree-tree'}> <div ref={(containerRef) => treeContainerRef.current = containerRef} className={'PgTree-tree'}>
<AutoSizer> <AutoSizer>
@ -83,9 +85,10 @@ export default function PgTreeView({ data = [], hasCheckbox = false, selectionCh
disableDrag={true} disableDrag={true}
disableDrop={true} disableDrop={true}
dndRootElement={treeContainerRef.current} dndRootElement={treeContainerRef.current}
{...props}
> >
{ {
(props) => <Node onNodeSelectionChange={onSelectionChange} hasCheckbox={hasCheckbox} {...props}></Node> (props) => <Node onNodeSelectionChange={onSelectionChange} hasCheckbox={hasCheckbox} {...props} />
} }
</Tree> </Tree>
)} )}
@ -93,7 +96,7 @@ export default function PgTreeView({ data = [], hasCheckbox = false, selectionCh
</div> </div>
</PgTreeSelectionContext.Provider> </PgTreeSelectionContext.Provider>
: :
<EmptyPanelMessage text={gettext('No objects are found to display')}/> <EmptyPanelMessage text={gettext('No objects are found to display')} />
} }
</Root> </Root>
); );
@ -103,22 +106,23 @@ PgTreeView.propTypes = {
data: PropTypes.array, data: PropTypes.array,
selectionChange: PropTypes.func, selectionChange: PropTypes.func,
hasCheckbox: PropTypes.bool, hasCheckbox: PropTypes.bool,
NodeComponent: PropTypes.func
}; };
function Node({ node, style, tree, hasCheckbox, onNodeSelectionChange}) { function DefaultNode({ node, style, tree, hasCheckbox, onNodeSelectionChange }) {
const pgTreeSelCtx = React.useContext(PgTreeSelectionContext); const pgTreeSelCtx = React.useContext(PgTreeSelectionContext);
const [isSelected, setIsSelected] = React.useState(pgTreeSelCtx.includes(node.id) || node.data?.isSelected); const [isSelected, setIsSelected] = React.useState(pgTreeSelCtx.includes(node.id) || node.data?.isSelected);
const [isIndeterminate, setIsIndeterminate] = React.useState(node?.parent.level==0); const [isIndeterminate, setIsIndeterminate] = React.useState(node?.parent.level == 0);
useEffect(()=>{ useEffect(() => {
setIsIndeterminate(node.data.isIndeterminate); setIsIndeterminate(node.data.isIndeterminate);
}, [node?.data?.isIndeterminate]); }, [node?.data?.isIndeterminate]);
useEffect(()=>{ useEffect(() => {
if(isSelected){ if (isSelected) {
if(!pgTreeSelCtx.includes(node.id)){ if (!pgTreeSelCtx.includes(node.id)) {
tree.selectMulti(node.id); tree.selectMulti(node.id);
onNodeSelectionChange(); onNodeSelectionChange();
} }
@ -139,7 +143,7 @@ function Node({ node, style, tree, hasCheckbox, onNodeSelectionChange}) {
checkAndSelectParent(node); checkAndSelectParent(node);
} }
if(node?.level == 0) { if (node?.level == 0) {
node.data.isIndeterminate = false; node.data.isIndeterminate = false;
} }
node.focus(); node.focus();
@ -149,7 +153,7 @@ function Node({ node, style, tree, hasCheckbox, onNodeSelectionChange}) {
deselectAllChild(node); deselectAllChild(node);
} }
if(node?.parent){ if (node?.parent) {
node.parent.data.isIndeterminate = false; node.parent.data.isIndeterminate = false;
delectPrentNode(node.parent); delectPrentNode(node.parent);
} }
@ -165,7 +169,7 @@ function Node({ node, style, tree, hasCheckbox, onNodeSelectionChange}) {
}; };
const onKeyDown = (e) => { const onKeyDown = (e) => {
if(e.code == 'Enter') { if (e.code == 'Enter') {
onSelect(e); onSelect(e);
} }
}; };
@ -174,10 +178,10 @@ function Node({ node, style, tree, hasCheckbox, onNodeSelectionChange}) {
<div style={style} className={node.isFocused ? 'PgTree-focusedNode' : ''} onClick={onSelect} onKeyDown={onKeyDown}> <div style={style} className={node.isFocused ? 'PgTree-focusedNode' : ''} onClick={onSelect} onKeyDown={onKeyDown}>
<CollectionArrow node={node} tree={tree} selectedNodeIds={pgTreeSelCtx} /> <CollectionArrow node={node} tree={tree} selectedNodeIds={pgTreeSelCtx} />
{ {
hasCheckbox ? <Checkbox style={{ padding: 0 }} color="primary" className={!node.isInternal ? 'PgTree-leafNode': null} hasCheckbox ? <Checkbox style={{ padding: 0 }} color="primary" className={!node.isInternal ? 'PgTree-leafNode' : null}
checked={isSelected} checked={isSelected}
checkedIcon={isIndeterminate ? <IndeterminateCheckBoxIcon style={{height: '1.4rem'}} />: <CheckBoxIcon style={{height: '1.4rem'}} />} checkedIcon={isIndeterminate ? <IndeterminateCheckBoxIcon style={{ height: '1.4rem' }} /> : <CheckBoxIcon style={{ height: '1.4rem' }} />}
onChange={onCheckboxSelection}/> : onChange={onCheckboxSelection} /> :
<span className={node.data.icon}></span> <span className={node.data.icon}></span>
} }
<div className={node.data.icon + ' PgTree-node'}>{node.data.name}</div> <div className={node.data.icon + ' PgTree-node'}>{node.data.name}</div>
@ -185,7 +189,7 @@ function Node({ node, style, tree, hasCheckbox, onNodeSelectionChange}) {
); );
} }
Node.propTypes = { DefaultNode.propTypes = {
node: PropTypes.object, node: PropTypes.object,
style: PropTypes.any, style: PropTypes.any,
tree: PropTypes.object, tree: PropTypes.object,
@ -202,7 +206,7 @@ function CollectionArrow({ node, tree, selectedNodeIds }) {
} }
}; };
return ( return (
<span onClick={toggleNode} onKeyDown={()=>{/* handled by parent */}}> <span onClick={toggleNode} onKeyDown={() => {/* handled by parent */ }}>
{node.isInternal && node?.children.length > 0 ? <ToggleArrowIcon node={node} /> : null} {node.isInternal && node?.children.length > 0 ? <ToggleArrowIcon node={node} /> : null}
</span> </span>
); );
@ -215,7 +219,7 @@ CollectionArrow.propTypes = {
}; };
function ToggleArrowIcon({node}){ function ToggleArrowIcon({ node }) {
return (<>{node.isOpen ? <ExpandMoreIcon /> : <ChevronRightIcon />}</>); return (<>{node.isOpen ? <ExpandMoreIcon /> : <ChevronRightIcon />}</>);
} }
@ -223,7 +227,7 @@ ToggleArrowIcon.propTypes = {
node: PropTypes.object, node: PropTypes.object,
}; };
function checkAndSelectParent(chNode){ function checkAndSelectParent(chNode) {
let isAllChildSelected = true; let isAllChildSelected = true;
chNode?.parent?.children?.forEach((child) => { chNode?.parent?.children?.forEach((child) => {
if (!child.isSelected) { if (!child.isSelected) {
@ -251,15 +255,15 @@ checkAndSelectParent.propTypes = {
chNode: PropTypes.object chNode: PropTypes.object
}; };
function delectPrentNode(chNode){ function delectPrentNode(chNode) {
if (chNode) { if (chNode) {
let isAnyChildSelected = false; let isAnyChildSelected = false;
chNode.children.forEach((childNode)=>{ chNode.children.forEach((childNode) => {
if(childNode.isSelected && !isAnyChildSelected){ if (childNode.isSelected && !isAnyChildSelected) {
isAnyChildSelected = true; isAnyChildSelected = true;
} }
}); });
if(isAnyChildSelected){ if (isAnyChildSelected) {
chNode.data.isSelected = true; chNode.data.isSelected = true;
chNode.data.isIndeterminate = true; chNode.data.isIndeterminate = true;
} else { } else {
@ -273,14 +277,14 @@ function delectPrentNode(chNode){
} }
} }
function selectAllChild(chNode, tree, source, selectedNodeIds){ function selectAllChild(chNode, tree, source, selectedNodeIds) {
let selectedChild = 0; let selectedChild = 0;
chNode?.children?.forEach(child => { chNode?.children?.forEach(child => {
if(!child.isLeaf) { if (!child.isLeaf) {
child.data.isIndeterminate = false; child.data.isIndeterminate = false;
} }
if((source == 'expand' && selectedNodeIds.includes(child.id)) || source == 'checkbox') { if ((source == 'expand' && selectedNodeIds.includes(child.id)) || source == 'checkbox') {
child.data.isSelected = true; child.data.isSelected = true;
selectedChild += 1; selectedChild += 1;
} }
@ -291,7 +295,7 @@ function selectAllChild(chNode, tree, source, selectedNodeIds){
} }
}); });
if(selectedChild < chNode?.children.length ){ if (selectedChild < chNode?.children.length) {
chNode.data.isIndeterminate = true; chNode.data.isIndeterminate = true;
} else { } else {
chNode.data.isIndeterminate = false; chNode.data.isIndeterminate = false;
@ -302,7 +306,7 @@ function selectAllChild(chNode, tree, source, selectedNodeIds){
} }
} }
function deselectAllChild(chNode){ function deselectAllChild(chNode) {
chNode?.children.forEach(child => { chNode?.children.forEach(child => {
child.deselect(child); child.deselect(child);
child.data.isSelected = false; child.data.isSelected = false;