@@ -8,6 +8,7 @@ import React, { Component, PropTypes } from 'react';
8
8
import { AutoSizer , List } from 'react-virtualized' ;
9
9
import 'react-virtualized/styles.css' ;
10
10
import TreeNode from './tree-node' ;
11
+ import NodeRendererDefault from './node-renderer-default' ;
11
12
import {
12
13
walk ,
13
14
getFlatDataFromTree ,
@@ -22,8 +23,6 @@ import {
22
23
} from './utils/generic-utils' ;
23
24
import {
24
25
defaultGetNodeKey ,
25
- defaultToggleChildrenVisibility ,
26
- defaultMoveNode ,
27
26
defaultSearchMethod ,
28
27
} from './utils/default-handlers' ;
29
28
import {
@@ -36,28 +35,7 @@ class ReactSortableTree extends Component {
36
35
constructor ( props ) {
37
36
super ( props ) ;
38
37
39
- if ( process . env . NODE_ENV === 'development' ) {
40
- /* eslint-disable no-console */
41
- const usesDefaultHandlers = (
42
- ! props . toggleChildrenVisibility
43
- ) ;
44
-
45
- if ( ! props . updateTreeData && usesDefaultHandlers ) {
46
- console . warn ( 'Need to add specify updateTreeData prop if default event handlers are used' ) ;
47
- }
48
- /* eslint-enable */
49
- }
50
-
51
- // Fall back to default event listeners if necessary and bind them to the tree
52
- this . getNodeKey = ( props . getNodeKey || defaultGetNodeKey ) . bind ( this ) ;
53
- this . moveNode = ( props . moveNode || defaultMoveNode ) . bind ( this ) ;
54
- this . toggleChildrenVisibility = (
55
- props . toggleChildrenVisibility || defaultToggleChildrenVisibility
56
- ) . bind ( this ) ;
57
- this . nodeContentRenderer = dndWrapSource (
58
- props . nodeContentRenderer ||
59
- require ( './node-renderer-default' ) . default // eslint-disable-line global-require
60
- ) ;
38
+ this . nodeContentRenderer = dndWrapSource ( props . nodeContentRenderer ) ;
61
39
62
40
this . state = {
63
41
draggingTreeData : null ,
@@ -69,6 +47,8 @@ class ReactSortableTree extends Component {
69
47
searchFocusTreeIndex : null ,
70
48
} ;
71
49
50
+ this . toggleChildrenVisibility = this . toggleChildrenVisibility . bind ( this ) ;
51
+ this . moveNode = this . moveNode . bind ( this ) ;
72
52
this . startDrag = this . startDrag . bind ( this ) ;
73
53
this . dragHover = this . dragHover . bind ( this ) ;
74
54
this . endDrag = this . endDrag . bind ( this ) ;
@@ -80,6 +60,41 @@ class ReactSortableTree extends Component {
80
60
this . ignoreOneTreeUpdate = false ;
81
61
}
82
62
63
+ toggleChildrenVisibility ( { node : targetNode , path, treeIndex : _treeIndex } ) {
64
+ const treeData = changeNodeAtPath ( {
65
+ treeData : this . props . treeData ,
66
+ path,
67
+ newNode : ( { node } ) => ( { ...node , expanded : ! node . expanded } ) ,
68
+ getNodeKey : this . props . getNodeKey ,
69
+ } ) ;
70
+
71
+ this . props . onChange ( treeData ) ;
72
+
73
+ if ( this . props . onVisibilityToggle ) {
74
+ this . props . onVisibilityToggle ( {
75
+ treeData,
76
+ node : targetNode ,
77
+ expanded : ! targetNode . expanded ,
78
+ } ) ;
79
+ }
80
+ }
81
+
82
+ moveNode ( { node, depth, minimumTreeIndex } ) {
83
+ const treeData = insertNode ( {
84
+ treeData : this . state . draggingTreeData ,
85
+ newNode : node ,
86
+ depth,
87
+ minimumTreeIndex,
88
+ expandParent : true ,
89
+ } ) . treeData ;
90
+
91
+ this . props . onChange ( treeData ) ;
92
+
93
+ if ( this . props . onMoveNode ) {
94
+ this . props . onMoveNode ( { treeData, node } ) ;
95
+ }
96
+ }
97
+
83
98
componentWillReceiveProps ( nextProps ) {
84
99
this . setState ( { searchFocusTreeIndex : null } ) ;
85
100
if ( this . props . treeData !== nextProps . treeData ) {
@@ -110,15 +125,15 @@ class ReactSortableTree extends Component {
110
125
getRows ( treeData ) {
111
126
return getFlatDataFromTree ( {
112
127
ignoreCollapsed : true ,
113
- getNodeKey : this . getNodeKey ,
128
+ getNodeKey : this . props . getNodeKey ,
114
129
treeData,
115
130
} ) ;
116
131
}
117
132
118
133
search ( props = this . props , seekIndex = true , expand = true , singleSearch = false ) {
119
134
const {
120
135
treeData,
121
- updateTreeData ,
136
+ onChange ,
122
137
searchFinishCallback,
123
138
searchQuery,
124
139
searchMethod,
@@ -144,7 +159,7 @@ class ReactSortableTree extends Component {
144
159
treeData : expandedTreeData ,
145
160
matches : searchMatches ,
146
161
} = find ( {
147
- getNodeKey : this . getNodeKey ,
162
+ getNodeKey : this . props . getNodeKey ,
148
163
treeData,
149
164
searchQuery,
150
165
searchMethod : searchMethod || defaultSearchMethod ,
@@ -156,7 +171,7 @@ class ReactSortableTree extends Component {
156
171
// Update the tree with data leaving all paths leading to matching nodes open
157
172
if ( expand ) {
158
173
this . ignoreOneTreeUpdate = true ; // Prevents infinite loop
159
- updateTreeData ( expandedTreeData ) ;
174
+ onChange ( expandedTreeData ) ;
160
175
}
161
176
162
177
if ( searchFinishCallback ) {
@@ -181,7 +196,7 @@ class ReactSortableTree extends Component {
181
196
const draggingTreeData = removeNodeAtPath ( {
182
197
treeData : this . props . treeData ,
183
198
path,
184
- getNodeKey : this . getNodeKey ,
199
+ getNodeKey : this . props . getNodeKey ,
185
200
} ) ;
186
201
187
202
this . setState ( {
@@ -213,7 +228,7 @@ class ReactSortableTree extends Component {
213
228
treeData : this . state . draggingTreeData ,
214
229
path : expandedParentPath . slice ( 0 , - 1 ) ,
215
230
newNode : ( { node } ) => ( { ...node , expanded : true } ) ,
216
- getNodeKey : this . getNodeKey ,
231
+ getNodeKey : this . props . getNodeKey ,
217
232
} ) ,
218
233
} ) ;
219
234
}
@@ -238,7 +253,7 @@ class ReactSortableTree extends Component {
238
253
loadLazyChildren ( props = this . props ) {
239
254
walk ( {
240
255
treeData : props . treeData ,
241
- getNodeKey : this . getNodeKey ,
256
+ getNodeKey : this . props . getNodeKey ,
242
257
callback : ( { node, path, lowerSiblingCounts, treeIndex } ) => {
243
258
// If the node has children defined by a function, and is either expanded
244
259
// or set to load even before expansion, run the function.
@@ -254,15 +269,15 @@ class ReactSortableTree extends Component {
254
269
treeIndex,
255
270
256
271
// Provide a helper to append the new data when it is received
257
- done : childrenArray => this . props . updateTreeData ( changeNodeAtPath ( {
272
+ done : childrenArray => this . props . onChange ( changeNodeAtPath ( {
258
273
treeData : this . props . treeData ,
259
274
path,
260
275
newNode : ( { node : oldNode } ) => (
261
276
// Only replace the old node if it's the one we set off to find children
262
277
// for in the first place
263
278
oldNode === node ? { ...oldNode , children : childrenArray } : oldNode
264
279
) ,
265
- getNodeKey : this . getNodeKey ,
280
+ getNodeKey : this . props . getNodeKey ,
266
281
} ) ) ,
267
282
} ) ;
268
283
}
@@ -373,46 +388,84 @@ class ReactSortableTree extends Component {
373
388
}
374
389
375
390
ReactSortableTree . propTypes = {
391
+ // Tree data in the following format:
392
+ // [{title: 'main', subtitle: 'sub'}, { title: 'value2', expanded: true, children: [{ title: 'value3') }] }]
393
+ // `title` is the primary label for the node
394
+ // `subtitle` is a secondary label for the node
395
+ // `expanded` shows children of the node if true, or hides them if false. Defaults to false.
396
+ // `children` is an array of child nodes belonging to the node.
376
397
treeData : PropTypes . arrayOf ( PropTypes . object ) . isRequired ,
377
398
378
- // Callback for move operation.
379
- // Called as moveNode({ node, path, parentPath, minimumTreeIndex })
380
- moveNode : PropTypes . func ,
381
-
382
399
// Style applied to the container wrapping the tree (style defaults to {height: '100%'})
383
400
style : PropTypes . object ,
401
+
402
+ // Class name for the container wrapping the tree
384
403
className : PropTypes . string ,
385
404
386
405
// Style applied to the inner, scrollable container (for padding, etc.)
387
406
innerStyle : PropTypes . object ,
388
407
389
- // Height of each node row, used for react-virtualized
408
+ // Used by react-virtualized
409
+ // Either a fixed row height (number) or a function that returns the
410
+ // height of a row given its index: `({ index: number }): number`
390
411
rowHeight : PropTypes . oneOfType ( [ PropTypes . number , PropTypes . func ] ) ,
391
412
413
+ // The width of the blocks containing the lines representing the structure of the tree.
392
414
scaffoldBlockPxWidth : PropTypes . number ,
393
415
416
+ // Maximum depth nodes can be inserted at. Defaults to infinite.
394
417
maxDepth : PropTypes . number ,
395
418
396
- // Search stuff
397
- searchQuery : PropTypes . any ,
398
- searchFocusOffset : PropTypes . number ,
399
- searchMethod : PropTypes . func , // eslint-disable-line react/no-unused-prop-types
419
+ // The method used to search nodes.
420
+ // Defaults to a function that uses the `searchQuery` string to search for nodes with
421
+ // matching `title` or `subtitle` values.
422
+ // NOTE: Changing `searchMethod` will not update the search, but changing the `searchQuery` will.
423
+ searchMethod : PropTypes . func , // eslint-disable-line react/no-unused-prop-types
424
+
425
+ // Used by the `searchMethod` to highlight and scroll to matched nodes.
426
+ // Should be a string for the default `searchMethod`, but can be anything when using a custom search.
427
+ searchQuery : PropTypes . any ,
428
+
429
+ // Outline the <`searchFocusOffset`>th node and scroll to it.
430
+ searchFocusOffset : PropTypes . number ,
431
+
432
+ // Get the nodes that match the search criteria. Used for counting total matches, etc.
400
433
searchFinishCallback : PropTypes . func , // eslint-disable-line react/no-unused-prop-types
401
434
435
+ // Generate an object with additional props to be passed to the node renderer.
436
+ // Use this for adding buttons via the `buttons` key,
437
+ // or additional `style` / `className` settings.
438
+ generateNodeProps : PropTypes . func ,
439
+
440
+ // Override the default component for rendering nodes (but keep the scaffolding generator)
441
+ // This is an advanced option for complete customization of the appearance.
442
+ // It is best to copy the component in `node-renderer-default.js` to use as a base, and customize as needed.
402
443
nodeContentRenderer : PropTypes . any ,
403
- generateNodeProps : PropTypes . func ,
404
444
405
- getNodeKey : PropTypes . func ,
406
- updateTreeData : PropTypes . func ,
407
- toggleChildrenVisibility : PropTypes . func ,
445
+ // Determine the unique key used to identify each node and
446
+ // generate the `path` array passed in callbacks.
447
+ // By default, returns the index in the tree (omitting hidden nodes).
448
+ getNodeKey : PropTypes . func ,
449
+
450
+ // Called whenever tree data changed.
451
+ // Just like with React input elements, you have to update your
452
+ // own component's data to see the changes reflected.
453
+ onChange : PropTypes . func . isRequired ,
454
+
455
+ // Called after node move operation.
456
+ onMoveNode : PropTypes . func ,
457
+
458
+ // Called after children nodes collapsed or expanded.
459
+ onVisibilityToggle : PropTypes . func ,
408
460
} ;
409
461
410
462
ReactSortableTree . defaultProps = {
463
+ getNodeKey : defaultGetNodeKey ,
464
+ nodeContentRenderer : NodeRendererDefault ,
411
465
rowHeight : 62 ,
466
+ scaffoldBlockPxWidth : 44 ,
412
467
style : { } ,
413
468
innerStyle : { } ,
414
- scaffoldBlockPxWidth : 44 ,
415
- loadCollapsedLazyChildren : false ,
416
469
searchQuery : null ,
417
470
} ;
418
471
0 commit comments