mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Make PgTreeView react component more customisable
This commit is contained in:
parent
d39fe78239
commit
c45fb47b08
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user