@@ -50,7 +50,7 @@ class CheckboxTree extends React.Component {
5050 super ( props ) ;
5151
5252 this . id = `rct-${ nanoid ( 7 ) } ` ;
53- this . nodes = { } ;
53+ this . flatNodes = { } ;
5454
5555 this . flattenNodes ( props . nodes ) ;
5656 this . unserializeLists ( {
@@ -71,20 +71,23 @@ class CheckboxTree extends React.Component {
7171 }
7272
7373 onCheck ( node ) {
74+ // node is object from TreeNode
7475 const { noCascade, onCheck } = this . props ;
7576
7677 this . toggleChecked ( node , node . checked , noCascade ) ;
7778 onCheck ( this . serializeList ( 'checked' ) , node ) ;
7879 }
7980
8081 onExpand ( node ) {
82+ // node is object from TreeNode
8183 const { onExpand } = this . props ;
8284
8385 this . toggleNode ( 'expanded' , node , node . expanded ) ;
8486 onExpand ( this . serializeList ( 'expanded' ) , node ) ;
8587 }
8688
8789 getFormattedNodes ( nodes ) {
90+ // not used
8891 return nodes . map ( ( node ) => {
8992 const formatted = { ...node } ;
9093
@@ -103,6 +106,7 @@ class CheckboxTree extends React.Component {
103106 }
104107
105108 getCheckState ( node , noCascade ) {
109+ // not used
106110 if ( node . children === null || noCascade ) {
107111 return node . checked ? 1 : 0 ;
108112 }
@@ -118,7 +122,27 @@ class CheckboxTree extends React.Component {
118122 return 0 ;
119123 }
120124
125+ getShallowCheckState ( node , noCascade ) {
126+ // node is from props.nodes
127+ const flatNode = this . flatNodes [ node . value ] ;
128+
129+ if ( flatNode . isLeaf || noCascade ) {
130+ return flatNode . checked ? 1 : 0 ;
131+ }
132+
133+ if ( node . children . every ( child => ( this . flatNodes [ child . value ] . checkState === 1 ) ) ) {
134+ return 1 ;
135+ }
136+
137+ if ( node . children . some ( child => ( this . flatNodes [ child . value ] . checkState > 0 ) ) ) {
138+ return 2 ;
139+ }
140+
141+ return 0 ;
142+ }
143+
121144 getDisabledState ( node , parent , disabledProp , noCascade ) {
145+ // node is from props.nodes
122146 if ( disabledProp ) {
123147 return true ;
124148 }
@@ -131,45 +155,56 @@ class CheckboxTree extends React.Component {
131155 }
132156
133157 toggleChecked ( node , isChecked , noCascade ) {
134- if ( node . children === null || noCascade ) {
158+ // node is object from TreeNode
159+ const flatNode = this . flatNodes [ node . value ] ;
160+
161+ if ( flatNode . isLeaf || noCascade ) {
135162 // Set the check status of a leaf node or an uncoupled parent
136163 this . toggleNode ( 'checked' , node , isChecked ) ;
137164 } else {
165+ const { children } = flatNode . self ;
138166 // Percolate check status down to all children
139- node . children . forEach ( ( child ) => {
140- this . toggleChecked ( child , isChecked ) ;
167+ children . forEach ( ( child ) => {
168+ this . toggleChecked ( child , isChecked , noCascade ) ;
141169 } ) ;
142170 }
143171 }
144172
145173 toggleNode ( key , node , toggleValue ) {
146- this . nodes [ node . value ] [ key ] = toggleValue ;
174+ this . flatNodes [ node . value ] [ key ] = toggleValue ;
147175 }
148176
149- flattenNodes ( nodes ) {
177+ flattenNodes ( nodes , parentNode = { } ) {
178+ // nodes are from props.nodes
150179 if ( ! Array . isArray ( nodes ) || nodes . length === 0 ) {
151180 return ;
152181 }
153182
154183 nodes . forEach ( ( node ) => {
155- this . nodes [ node . value ] = { } ;
156- this . flattenNodes ( node . children ) ;
184+ // set defaults, calculated values and tree references
185+ this . flatNodes [ node . value ] = {
186+ parent : parentNode ,
187+ self : node ,
188+ isLeaf : ! Array . isArray ( node . children ) || node . children . length === 0 ,
189+ showCheckbox : node . showCheckbox !== undefined ? node . showCheckbox : true ,
190+ } ;
191+ this . flattenNodes ( node . children , node ) ;
157192 } ) ;
158193 }
159194
160195 unserializeLists ( lists ) {
161196 // Reset values to false
162- Object . keys ( this . nodes ) . forEach ( ( value ) => {
197+ Object . keys ( this . flatNodes ) . forEach ( ( value ) => {
163198 Object . keys ( lists ) . forEach ( ( listKey ) => {
164- this . nodes [ value ] [ listKey ] = false ;
199+ this . flatNodes [ value ] [ listKey ] = false ;
165200 } ) ;
166201 } ) ;
167202
168203 // Unserialize values and set their nodes to true
169204 Object . keys ( lists ) . forEach ( ( listKey ) => {
170205 lists [ listKey ] . forEach ( ( value ) => {
171- if ( this . nodes [ value ] !== undefined ) {
172- this . nodes [ value ] [ listKey ] = true ;
206+ if ( this . flatNodes [ value ] !== undefined ) {
207+ this . flatNodes [ value ] [ listKey ] = true ;
173208 }
174209 } ) ;
175210 } ) ;
@@ -178,8 +213,8 @@ class CheckboxTree extends React.Component {
178213 serializeList ( key ) {
179214 const list = [ ] ;
180215
181- Object . keys ( this . nodes ) . forEach ( ( value ) => {
182- if ( this . nodes [ value ] [ key ] ) {
216+ Object . keys ( this . flatNodes ) . forEach ( ( value ) => {
217+ if ( this . flatNodes [ value ] [ key ] ) {
183218 list . push ( value ) ;
184219 }
185220 } ) ;
@@ -188,6 +223,7 @@ class CheckboxTree extends React.Component {
188223 }
189224
190225 isEveryChildChecked ( node ) {
226+ // not used
191227 return node . children . every ( ( child ) => {
192228 if ( child . children !== null ) {
193229 return this . isEveryChildChecked ( child ) ;
@@ -198,6 +234,7 @@ class CheckboxTree extends React.Component {
198234 }
199235
200236 isSomeChildChecked ( node ) {
237+ // not used
201238 return node . children . some ( ( child ) => {
202239 if ( child . children !== null ) {
203240 return this . isSomeChildChecked ( child ) ;
@@ -208,6 +245,7 @@ class CheckboxTree extends React.Component {
208245 }
209246
210247 renderTreeNodes ( nodes , parent = { } ) {
248+ // nodes are props.nodes
211249 const {
212250 disabled,
213251 expandDisabled,
@@ -218,39 +256,57 @@ class CheckboxTree extends React.Component {
218256 showNodeIcon,
219257 onClick,
220258 } = this . props ;
259+
221260 const treeNodes = nodes . map ( ( node ) => {
222261 const key = `${ node . value } ` ;
223- const checked = this . getCheckState ( node , noCascade ) ;
224- const isLeaf = node . children === null ;
225- const children = this . renderChildNodes ( node ) ;
262+
263+ const flatNode = this . flatNodes [ node . value ] ;
264+
265+ let children = null ;
266+ if ( ! flatNode . isLeaf ) {
267+ children = this . renderTreeNodes ( node . children , node ) ;
268+ }
269+
270+ // set checkState here
271+ // this can be "shallow" because checkState is updated for all
272+ // nested children in the recursive call to renderTreeNodes above
273+ flatNode . checkState = this . getShallowCheckState ( node , noCascade ) ;
274+
226275 const nodeDisabled = this . getDisabledState ( node , parent , disabled , noCascade ) ;
276+
227277 // Show checkbox only if this is a leaf node or showCheckbox is true
228- const showCheckbox = onlyLeafCheckboxes ? isLeaf : node . showCheckbox ;
229-
230- return (
231- < TreeNode
232- key = { key }
233- checked = { checked }
234- className = { node . className }
235- disabled = { nodeDisabled }
236- expandDisabled = { expandDisabled }
237- expandOnClick = { expandOnClick }
238- expanded = { node . expanded }
239- icon = { node . icon }
240- label = { node . label }
241- optimisticToggle = { optimisticToggle }
242- rawChildren = { node . children }
243- showCheckbox = { showCheckbox }
244- showNodeIcon = { showNodeIcon }
245- treeId = { this . id }
246- value = { node . value }
247- onCheck = { this . onCheck }
248- onClick = { onClick }
249- onExpand = { this . onExpand }
250- >
251- { children }
252- </ TreeNode >
253- ) ;
278+ const showCheckbox = onlyLeafCheckboxes ? flatNode . isLeaf : flatNode . showCheckbox ;
279+
280+ // root of tree has no parent value and is expanded by default
281+ const parentExpanded = parent . value ? this . flatNodes [ parent . value ] . expanded : true ;
282+ if ( parentExpanded ) {
283+ return (
284+ < TreeNode
285+ key = { key }
286+ checked = { flatNode . checkState }
287+ className = { node . className }
288+ disabled = { nodeDisabled }
289+ expandDisabled = { expandDisabled }
290+ expandOnClick = { expandOnClick }
291+ expanded = { flatNode . expanded }
292+ icon = { node . icon }
293+ label = { node . label }
294+ optimisticToggle = { optimisticToggle }
295+ isLeaf = { flatNode . isLeaf }
296+ showCheckbox = { showCheckbox }
297+ showNodeIcon = { showNodeIcon }
298+ treeId = { this . id }
299+ value = { node . value }
300+ onCheck = { this . onCheck }
301+ onClick = { onClick }
302+ onExpand = { this . onExpand }
303+ >
304+ { children }
305+ </ TreeNode >
306+ ) ;
307+ }
308+
309+ return null ;
254310 } ) ;
255311
256312 return (
@@ -261,6 +317,8 @@ class CheckboxTree extends React.Component {
261317 }
262318
263319 renderChildNodes ( node ) {
320+ // not used
321+ // node is from props.nodes
264322 if ( node . children !== null && node . expanded ) {
265323 return this . renderTreeNodes ( node . children , node ) ;
266324 }
@@ -295,8 +353,8 @@ class CheckboxTree extends React.Component {
295353 }
296354
297355 render ( ) {
298- const nodes = this . getFormattedNodes ( this . props . nodes ) ;
299- const treeNodes = this . renderTreeNodes ( nodes ) ;
356+ const treeNodes = this . renderTreeNodes ( this . props . nodes ) ;
357+
300358 const className = classNames ( {
301359 'react-checkbox-tree' : true ,
302360 'rct-disabled' : this . props . disabled ,
0 commit comments