@@ -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