Skip to content

Commit 3969615

Browse files
Fix tree view checkbox selection for backup dialog. #9649
1 parent 965a27d commit 3969615

1 file changed

Lines changed: 48 additions & 10 deletions

File tree

web/pgadmin/static/js/PgTreeView/index.jsx

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,39 +80,77 @@ export default function PgTreeView({ data = [], hasCheckbox = false,
8080
const [checkedState, setCheckedState] = React.useState({});
8181
const { ref: containerRef, width, height } = useResizeObserver();
8282

83+
// Handle checkbox toggle and collect all checked nodes
84+
// to pass complete selection state to the backup dialog
8385
const toggleCheck = (node, isChecked) => {
8486
const newState = { ...checkedState };
85-
const selectedChNodes = [];
8687

87-
// Update the node itself and all descendants
88+
// Update the clicked node and all its descendants with the new checked value
8889
const updateDescendants = (n, val) => {
8990
newState[n.id] = val;
90-
if (val) {
91-
selectedChNodes.push(n);
92-
}
9391
n.children?.forEach(child => updateDescendants(child, val));
9492
};
9593
updateDescendants(node, isChecked);
9694

97-
// Update ancestors (Indeterminate logic)
95+
// Update ancestor nodes to reflect the correct state (checked/unchecked/indeterminate)
96+
// This ensures parent nodes show proper visual feedback based on children's state
9897
let parent = node.parent;
9998
while (parent && parent.id !== '__root__') {
100-
const allChecked = parent.children.every(c => newState[c.id]);
99+
// Check if ALL children are fully checked (state must be exactly true,
100+
// not 'indeterminate') to mark parent as fully checked
101+
const allChecked = parent.children.every(c => newState[c.id] === true);
102+
// Check if ALL children are unchecked (falsy value: false, undefined, or null)
101103
const noneChecked = parent.children.every(c => !newState[c.id]);
102104

103105
if (allChecked) {
106+
// All children checked -> parent is fully checked
104107
newState[parent.id] = true;
105-
// logic for custom indeterminate property if needed
106108
} else if (noneChecked) {
109+
// No children checked -> parent is unchecked
107110
newState[parent.id] = false;
108111
} else {
109-
newState[parent.id] = 'indeterminate'; // Store string for 3rd state
112+
// Some children checked, some not -> parent shows indeterminate state
113+
newState[parent.id] = 'indeterminate';
110114
}
111115
parent = parent.parent;
112116
}
113117

114118
setCheckedState(newState);
115-
selectionChange?.(selectedChNodes);
119+
120+
// Collect all checked/indeterminate nodes from the entire tree
121+
// to provide complete selection state to selectionChange callback.
122+
const allCheckedNodes = [];
123+
const collectAllCheckedNodes = (n) => {
124+
if (!n) return;
125+
const state = newState[n.id];
126+
if (state === true || state === 'indeterminate') {
127+
// Set isIndeterminate flag to differentiate full schema selection
128+
// from partial selection (only specific tables) in backup dialog
129+
n.data.isIndeterminate = (state === 'indeterminate');
130+
allCheckedNodes.push(n);
131+
}
132+
// Recursively check all children
133+
n.children?.forEach(child => collectAllCheckedNodes(child));
134+
};
135+
136+
// Navigate up to find the root level of the tree (parent of root nodes is '__root__')
137+
let rootNode = node;
138+
while (rootNode.parent && rootNode.parent.id !== '__root__') {
139+
rootNode = rootNode.parent;
140+
}
141+
142+
// Traverse all root-level nodes to collect checked nodes from entire tree
143+
const rootParent = rootNode.parent;
144+
if (rootParent && rootParent.children) {
145+
// Iterate through all sibling root nodes to collect all checked nodes
146+
rootParent.children.forEach(root => collectAllCheckedNodes(root));
147+
} else {
148+
// Fallback: if we can't find siblings, just traverse from the found root
149+
collectAllCheckedNodes(rootNode);
150+
}
151+
152+
// Pass all checked nodes to callback with current selection state.
153+
selectionChange?.(allCheckedNodes);
116154
};
117155

118156
return (<Root ref={containerRef} className={'PgTree-tree'}>

0 commit comments

Comments
 (0)