Skip to content

Commit 48b270f

Browse files
committed
Merge branch 'worthlutz-refactor' into v1.3-dev-merge
# Conflicts: # src/js/CheckboxTree.js # src/js/TreeNode.js # test/TreeNode.js
2 parents 44dce3e + 7afe2d4 commit 48b270f

File tree

6 files changed

+109
-127
lines changed

6 files changed

+109
-127
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
/node_modules
44
package-lock.json
55
yarn.lock
6+
package-lock.json

examples/dist/index.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/js/CheckboxTree.js

Lines changed: 88 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class CheckboxTree extends React.Component {
7777
super(props);
7878

7979
this.id = `rct-${nanoid(7)}`;
80-
this.nodes = {};
80+
this.flatNodes = {};
8181

8282
this.flattenNodes(props.nodes);
8383
this.deserializeLists({
@@ -100,13 +100,15 @@ class CheckboxTree extends React.Component {
100100
}
101101

102102
onCheck(node) {
103+
// node is object from TreeNode
103104
const { noCascade, onCheck } = this.props;
104105

105106
this.toggleChecked(node, node.checked, noCascade);
106107
onCheck(this.serializeList('checked'), node);
107108
}
108109

109110
onExpand(node) {
111+
// node is object from TreeNode
110112
const { onExpand } = this.props;
111113

112114
this.toggleNode('expanded', node, node.expanded);
@@ -121,43 +123,27 @@ class CheckboxTree extends React.Component {
121123
this.expandAllNodes(false);
122124
}
123125

124-
getFormattedNodes(nodes) {
125-
const nodeMap = this.nodes;
126+
getShallowCheckState(node, noCascade) {
127+
// node is from props.nodes
128+
const flatNode = this.flatNodes[node.value];
126129

127-
return nodes.map((node) => {
128-
const formatted = { ...node };
129-
130-
formatted.checked = nodeMap[node.value].checked;
131-
formatted.expanded = nodeMap[node.value].expanded;
132-
formatted.showCheckbox = node.showCheckbox !== undefined ? node.showCheckbox : true;
133-
134-
if (nodeMap[node.value].isParent) {
135-
formatted.children = this.getFormattedNodes(formatted.children);
136-
} else {
137-
formatted.children = null;
138-
}
139-
140-
return formatted;
141-
});
142-
}
143-
144-
getCheckState(node, noCascade) {
145-
if (node.children === null || noCascade) {
146-
return node.checked ? 1 : 0;
130+
if (flatNode.isLeaf || noCascade) {
131+
return flatNode.checked ? 1 : 0;
147132
}
148133

149-
if (this.isEveryChildChecked(node)) {
134+
if (node.children.every(child => (this.flatNodes[child.value].checkState === 1))) {
150135
return 1;
151136
}
152137

153-
if (this.isSomeChildChecked(node)) {
138+
if (node.children.some(child => (this.flatNodes[child.value].checkState > 0))) {
154139
return 2;
155140
}
156141

157142
return 0;
158143
}
159144

160145
getDisabledState(node, parent, disabledProp, noCascade) {
146+
// node is from props.nodes
161147
if (disabledProp) {
162148
return true;
163149
}
@@ -176,60 +162,68 @@ class CheckboxTree extends React.Component {
176162
expandAllNodes(expand = true) {
177163
const { onExpand } = this.props;
178164

179-
Object.keys(this.nodes).forEach((value) => {
180-
if (this.nodes[value].isParent) {
181-
this.nodes[value].expanded = expand;
165+
Object.keys(this.flatNodes).forEach((value) => {
166+
if (this.flatNodes[value].isParent) {
167+
this.flatNodes[value].expanded = expand;
182168
}
183169
});
184170

185171
onExpand(this.serializeList('expanded', null));
186172
}
187173

188174
toggleChecked(node, isChecked, noCascade) {
189-
if (node.children === null || noCascade) {
175+
// node is object from TreeNode
176+
const flatNode = this.flatNodes[node.value];
177+
178+
if (flatNode.isLeaf || noCascade) {
190179
// Set the check status of a leaf node or an uncoupled parent
191180
this.toggleNode('checked', node, isChecked);
192181
} else {
182+
const { children } = flatNode.self;
193183
// Percolate check status down to all children
194-
node.children.forEach((child) => {
195-
this.toggleChecked(child, isChecked);
184+
children.forEach((child) => {
185+
this.toggleChecked(child, isChecked, noCascade);
196186
});
197187
}
198188
}
199189

200190
toggleNode(key, node, toggleValue) {
201-
this.nodes[node.value][key] = toggleValue;
191+
this.flatNodes[node.value][key] = toggleValue;
202192
}
203193

204-
flattenNodes(nodes) {
194+
flattenNodes(nodes, parentNode = {}) {
195+
// nodes are from props.nodes
205196
if (!Array.isArray(nodes) || nodes.length === 0) {
206197
return;
207198
}
208199

209200
nodes.forEach((node) => {
210201
const isParent = this.nodeHasChildren(node);
211202

212-
this.nodes[node.value] = {
203+
this.flatNodes[node.value] = {
213204
isParent,
214205
isLeaf: !isParent,
206+
parent: parentNode,
207+
self: node,
208+
showCheckbox: node.showCheckbox !== undefined ? node.showCheckbox : true,
215209
};
216-
this.flattenNodes(node.children);
210+
this.flattenNodes(node.children, node);
217211
});
218212
}
219213

220214
deserializeLists(lists) {
221215
// Reset values to false
222-
Object.keys(this.nodes).forEach((value) => {
216+
Object.keys(this.flatNodes).forEach((value) => {
223217
Object.keys(lists).forEach((listKey) => {
224-
this.nodes[value][listKey] = false;
218+
this.flatNodes[value][listKey] = false;
225219
});
226220
});
227221

228222
// Deserialize values and set their nodes to true
229223
Object.keys(lists).forEach((listKey) => {
230224
lists[listKey].forEach((value) => {
231-
if (this.nodes[value] !== undefined) {
232-
this.nodes[value][listKey] = true;
225+
if (this.flatNodes[value] !== undefined) {
226+
this.flatNodes[value][listKey] = true;
233227
}
234228
});
235229
});
@@ -238,36 +232,17 @@ class CheckboxTree extends React.Component {
238232
serializeList(key) {
239233
const list = [];
240234

241-
Object.keys(this.nodes).forEach((value) => {
242-
if (this.nodes[value][key]) {
235+
Object.keys(this.flatNodes).forEach((value) => {
236+
if (this.flatNodes[value][key]) {
243237
list.push(value);
244238
}
245239
});
246240

247241
return list;
248242
}
249243

250-
isEveryChildChecked(node) {
251-
return node.children.every((child) => {
252-
if (child.children !== null) {
253-
return this.isEveryChildChecked(child);
254-
}
255-
256-
return child.checked;
257-
});
258-
}
259-
260-
isSomeChildChecked(node) {
261-
return node.children.some((child) => {
262-
if (child.children !== null) {
263-
return this.isSomeChildChecked(child);
264-
}
265-
266-
return child.checked;
267-
});
268-
}
269-
270244
renderTreeNodes(nodes, parent = {}) {
245+
// nodes are props.nodes
271246
const {
272247
disabled,
273248
expandDisabled,
@@ -282,42 +257,60 @@ class CheckboxTree extends React.Component {
282257
onClick,
283258
} = this.props;
284259
const { icons: defaultIcons } = CheckboxTree.defaultProps;
260+
285261
const treeNodes = nodes.map((node) => {
286262
const key = `${node.value}`;
287-
const checked = this.getCheckState(node, noCascade);
288-
const isLeaf = node.children === null;
289-
const children = this.renderChildNodes(node);
263+
264+
const flatNode = this.flatNodes[node.value];
265+
266+
let children = null;
267+
if (!flatNode.isLeaf) {
268+
children = this.renderTreeNodes(node.children, node);
269+
}
270+
271+
// set checkState here
272+
// this can be "shallow" because checkState is updated for all
273+
// nested children in the recursive call to renderTreeNodes above
274+
flatNode.checkState = this.getShallowCheckState(node, noCascade);
275+
290276
const nodeDisabled = this.getDisabledState(node, parent, disabled, noCascade);
277+
291278
// Show checkbox only if this is a leaf node or showCheckbox is true
292-
const showCheckbox = onlyLeafCheckboxes ? isLeaf : node.showCheckbox;
293-
294-
return (
295-
<TreeNode
296-
key={key}
297-
checked={checked}
298-
className={node.className}
299-
disabled={nodeDisabled}
300-
expandDisabled={expandDisabled}
301-
expandOnClick={expandOnClick}
302-
expanded={node.expanded}
303-
icon={node.icon}
304-
icons={{ ...defaultIcons, ...icons }}
305-
label={node.label}
306-
lang={lang}
307-
optimisticToggle={optimisticToggle}
308-
rawChildren={node.children}
309-
showCheckbox={showCheckbox}
310-
showNodeIcon={showNodeIcon}
311-
title={showNodeTitle ? node.title || node.label : node.title}
312-
treeId={this.id}
313-
value={node.value}
314-
onCheck={this.onCheck}
315-
onClick={onClick}
316-
onExpand={this.onExpand}
317-
>
318-
{children}
319-
</TreeNode>
320-
);
279+
const showCheckbox = onlyLeafCheckboxes ? flatNode.isLeaf : flatNode.showCheckbox;
280+
281+
// root of tree has no parent value and is expanded by default
282+
const parentExpanded = parent.value ? this.flatNodes[parent.value].expanded : true;
283+
if (parentExpanded) {
284+
return (
285+
<TreeNode
286+
key={key}
287+
checked={flatNode.checkState}
288+
className={node.className}
289+
disabled={nodeDisabled}
290+
expandDisabled={expandDisabled}
291+
expandOnClick={expandOnClick}
292+
expanded={flatNode.expanded}
293+
icon={node.icon}
294+
icons={{ ...defaultIcons, ...icons }}
295+
label={node.label}
296+
lang={lang}
297+
optimisticToggle={optimisticToggle}
298+
isLeaf={flatNode.isLeaf}
299+
showCheckbox={showCheckbox}
300+
showNodeIcon={showNodeIcon}
301+
title={showNodeTitle ? node.title || node.label : node.title}
302+
treeId={this.id}
303+
value={node.value}
304+
onCheck={this.onCheck}
305+
onClick={onClick}
306+
onExpand={this.onExpand}
307+
>
308+
{children}
309+
</TreeNode>
310+
);
311+
}
312+
313+
return null;
321314
});
322315

323316
return (
@@ -327,14 +320,6 @@ class CheckboxTree extends React.Component {
327320
);
328321
}
329322

330-
renderChildNodes(node) {
331-
if (node.children !== null && node.expanded) {
332-
return this.renderTreeNodes(node.children, node);
333-
}
334-
335-
return null;
336-
}
337-
338323
renderExpandAll() {
339324
const { icons: { expandAll, collapseAll }, lang, showExpandAll } = this.props;
340325

@@ -395,7 +380,8 @@ class CheckboxTree extends React.Component {
395380

396381
render() {
397382
const { disabled, nodes, nativeCheckboxes } = this.props;
398-
const treeNodes = this.renderTreeNodes(this.getFormattedNodes(nodes));
383+
const treeNodes = this.renderTreeNodes(nodes);
384+
399385
const className = classNames({
400386
'react-checkbox-tree': true,
401387
'rct-disabled': disabled,

0 commit comments

Comments
 (0)