1
+ import { isEqual } from 'lodash' ;
1
2
import PropTypes from 'prop-types' ;
2
3
import React from 'react' ;
3
4
import shortid from 'shortid' ;
@@ -34,32 +35,47 @@ class CheckboxTree extends React.Component {
34
35
constructor ( props ) {
35
36
super ( props ) ;
36
37
38
+ this . id = `rct-${ shortid . generate ( ) } ` ;
39
+ this . nodes = { } ;
40
+
41
+ this . flattenNodes ( props . nodes ) ;
42
+ this . unserializeLists ( {
43
+ checked : props . checked ,
44
+ expanded : props . expanded ,
45
+ } ) ;
46
+
37
47
this . onCheck = this . onCheck . bind ( this ) ;
38
48
this . onExpand = this . onExpand . bind ( this ) ;
49
+ }
39
50
40
- this . id = `rct-${ shortid . generate ( ) } ` ;
51
+ componentWillReceiveProps ( { nodes, checked, expanded } ) {
52
+ if ( ! isEqual ( this . props . nodes , nodes ) ) {
53
+ this . flattenNodes ( nodes ) ;
54
+ }
55
+
56
+ this . unserializeLists ( { checked, expanded } ) ;
41
57
}
42
58
43
59
onCheck ( node ) {
44
- const { checked , onCheck } = this . props ;
60
+ const { onCheck } = this . props ;
45
61
46
- onCheck ( this . toggleChecked ( [ ...checked ] , node , node . checked ) ) ;
62
+ this . toggleChecked ( node , node . checked ) ;
63
+ onCheck ( this . serializeList ( 'checked' ) ) ;
47
64
}
48
65
49
66
onExpand ( node ) {
50
- const { expanded , onExpand } = this . props ;
67
+ const { onExpand } = this . props ;
51
68
52
- onExpand ( this . toggleNode ( [ ...expanded ] , node , node . expanded ) ) ;
69
+ this . toggleNode ( 'expanded' , node , node . expanded ) ;
70
+ onExpand ( this . serializeList ( 'expanded' ) ) ;
53
71
}
54
72
55
73
getFormattedNodes ( nodes ) {
56
- const { checked, expanded } = this . props ;
57
-
58
74
return nodes . map ( ( node ) => {
59
75
const formatted = { ...node } ;
60
76
61
- formatted . checked = checked . indexOf ( node . value ) > - 1 ;
62
- formatted . expanded = expanded . indexOf ( node . value ) > - 1 ;
77
+ formatted . checked = this . nodes [ node . value ] . checked ;
78
+ formatted . expanded = this . nodes [ node . value ] . expanded ;
63
79
64
80
if ( Array . isArray ( node . children ) && node . children . length > 0 ) {
65
81
formatted . children = this . getFormattedNodes ( formatted . children ) ;
@@ -87,29 +103,58 @@ class CheckboxTree extends React.Component {
87
103
return 0 ;
88
104
}
89
105
90
- toggleChecked ( checked , node , isChecked ) {
106
+ toggleChecked ( node , isChecked ) {
91
107
if ( node . children !== null ) {
92
108
// Percolate check status down to all children
93
109
node . children . forEach ( ( child ) => {
94
- this . toggleChecked ( checked , child , isChecked ) ;
110
+ this . toggleChecked ( child , isChecked ) ;
95
111
} ) ;
96
112
} else {
97
113
// Set leaf to check/unchecked state
98
- this . toggleNode ( checked , node , isChecked ) ;
114
+ this . toggleNode ( ' checked' , node , isChecked ) ;
99
115
}
100
-
101
- return checked ;
102
116
}
103
117
104
- toggleNode ( list , node , toggleValue ) {
105
- const index = list . indexOf ( node . value ) ;
118
+ toggleNode ( key , node , toggleValue ) {
119
+ this . nodes [ node . value ] [ key ] = toggleValue ;
120
+ }
106
121
107
- if ( index > - 1 && ! toggleValue ) {
108
- list . splice ( index , 1 ) ;
109
- } else if ( index === - 1 && toggleValue ) {
110
- list . push ( node . value ) ;
122
+ flattenNodes ( nodes ) {
123
+ if ( ! Array . isArray ( nodes ) || nodes . length === 0 ) {
124
+ return ;
111
125
}
112
126
127
+ nodes . forEach ( ( node ) => {
128
+ this . nodes [ node . value ] = { } ;
129
+ this . flattenNodes ( node . children ) ;
130
+ } ) ;
131
+ }
132
+
133
+ unserializeLists ( lists ) {
134
+ // Reset values to false
135
+ Object . keys ( this . nodes ) . forEach ( ( value ) => {
136
+ Object . keys ( lists ) . forEach ( ( listKey ) => {
137
+ this . nodes [ value ] [ listKey ] = false ;
138
+ } ) ;
139
+ } ) ;
140
+
141
+ // Unserialize values and set their nodes to true
142
+ Object . keys ( lists ) . forEach ( ( listKey ) => {
143
+ lists [ listKey ] . forEach ( ( value ) => {
144
+ this . nodes [ value ] [ listKey ] = true ;
145
+ } ) ;
146
+ } ) ;
147
+ }
148
+
149
+ serializeList ( key ) {
150
+ const list = [ ] ;
151
+
152
+ Object . keys ( this . nodes ) . forEach ( ( value ) => {
153
+ if ( this . nodes [ value ] [ key ] ) {
154
+ list . push ( value ) ;
155
+ }
156
+ } ) ;
157
+
113
158
return list ;
114
159
}
115
160
@@ -134,8 +179,8 @@ class CheckboxTree extends React.Component {
134
179
}
135
180
136
181
renderTreeNodes ( nodes ) {
137
- const treeNodes = nodes . map ( ( node , index ) => {
138
- const key = `${ index } - ${ node . value } ` ;
182
+ const treeNodes = nodes . map ( ( node ) => {
183
+ const key = `${ node . value } ` ;
139
184
const checked = this . getCheckState ( node ) ;
140
185
const children = this . renderChildNodes ( node ) ;
141
186
0 commit comments