@@ -8,6 +8,7 @@ import React, { Component, PropTypes } from 'react';
88import { AutoSizer , List } from 'react-virtualized' ;
99import 'react-virtualized/styles.css' ;
1010import TreeNode from './tree-node' ;
11+ import NodeRendererDefault from './node-renderer-default' ;
1112import {
1213 walk ,
1314 getFlatDataFromTree ,
@@ -22,8 +23,6 @@ import {
2223} from './utils/generic-utils' ;
2324import {
2425 defaultGetNodeKey ,
25- defaultToggleChildrenVisibility ,
26- defaultMoveNode ,
2726 defaultSearchMethod ,
2827} from './utils/default-handlers' ;
2928import {
@@ -36,28 +35,7 @@ class ReactSortableTree extends Component {
3635 constructor ( props ) {
3736 super ( props ) ;
3837
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 ) ;
6139
6240 this . state = {
6341 draggingTreeData : null ,
@@ -69,6 +47,8 @@ class ReactSortableTree extends Component {
6947 searchFocusTreeIndex : null ,
7048 } ;
7149
50+ this . toggleChildrenVisibility = this . toggleChildrenVisibility . bind ( this ) ;
51+ this . moveNode = this . moveNode . bind ( this ) ;
7252 this . startDrag = this . startDrag . bind ( this ) ;
7353 this . dragHover = this . dragHover . bind ( this ) ;
7454 this . endDrag = this . endDrag . bind ( this ) ;
@@ -80,6 +60,41 @@ class ReactSortableTree extends Component {
8060 this . ignoreOneTreeUpdate = false ;
8161 }
8262
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+
8398 componentWillReceiveProps ( nextProps ) {
8499 this . setState ( { searchFocusTreeIndex : null } ) ;
85100 if ( this . props . treeData !== nextProps . treeData ) {
@@ -110,15 +125,15 @@ class ReactSortableTree extends Component {
110125 getRows ( treeData ) {
111126 return getFlatDataFromTree ( {
112127 ignoreCollapsed : true ,
113- getNodeKey : this . getNodeKey ,
128+ getNodeKey : this . props . getNodeKey ,
114129 treeData,
115130 } ) ;
116131 }
117132
118133 search ( props = this . props , seekIndex = true , expand = true , singleSearch = false ) {
119134 const {
120135 treeData,
121- updateTreeData ,
136+ onChange ,
122137 searchFinishCallback,
123138 searchQuery,
124139 searchMethod,
@@ -144,7 +159,7 @@ class ReactSortableTree extends Component {
144159 treeData : expandedTreeData ,
145160 matches : searchMatches ,
146161 } = find ( {
147- getNodeKey : this . getNodeKey ,
162+ getNodeKey : this . props . getNodeKey ,
148163 treeData,
149164 searchQuery,
150165 searchMethod : searchMethod || defaultSearchMethod ,
@@ -156,7 +171,7 @@ class ReactSortableTree extends Component {
156171 // Update the tree with data leaving all paths leading to matching nodes open
157172 if ( expand ) {
158173 this . ignoreOneTreeUpdate = true ; // Prevents infinite loop
159- updateTreeData ( expandedTreeData ) ;
174+ onChange ( expandedTreeData ) ;
160175 }
161176
162177 if ( searchFinishCallback ) {
@@ -181,7 +196,7 @@ class ReactSortableTree extends Component {
181196 const draggingTreeData = removeNodeAtPath ( {
182197 treeData : this . props . treeData ,
183198 path,
184- getNodeKey : this . getNodeKey ,
199+ getNodeKey : this . props . getNodeKey ,
185200 } ) ;
186201
187202 this . setState ( {
@@ -213,7 +228,7 @@ class ReactSortableTree extends Component {
213228 treeData : this . state . draggingTreeData ,
214229 path : expandedParentPath . slice ( 0 , - 1 ) ,
215230 newNode : ( { node } ) => ( { ...node , expanded : true } ) ,
216- getNodeKey : this . getNodeKey ,
231+ getNodeKey : this . props . getNodeKey ,
217232 } ) ,
218233 } ) ;
219234 }
@@ -238,7 +253,7 @@ class ReactSortableTree extends Component {
238253 loadLazyChildren ( props = this . props ) {
239254 walk ( {
240255 treeData : props . treeData ,
241- getNodeKey : this . getNodeKey ,
256+ getNodeKey : this . props . getNodeKey ,
242257 callback : ( { node, path, lowerSiblingCounts, treeIndex } ) => {
243258 // If the node has children defined by a function, and is either expanded
244259 // or set to load even before expansion, run the function.
@@ -254,15 +269,15 @@ class ReactSortableTree extends Component {
254269 treeIndex,
255270
256271 // 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 ( {
258273 treeData : this . props . treeData ,
259274 path,
260275 newNode : ( { node : oldNode } ) => (
261276 // Only replace the old node if it's the one we set off to find children
262277 // for in the first place
263278 oldNode === node ? { ...oldNode , children : childrenArray } : oldNode
264279 ) ,
265- getNodeKey : this . getNodeKey ,
280+ getNodeKey : this . props . getNodeKey ,
266281 } ) ) ,
267282 } ) ;
268283 }
@@ -373,46 +388,84 @@ class ReactSortableTree extends Component {
373388}
374389
375390ReactSortableTree . 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.
376397 treeData : PropTypes . arrayOf ( PropTypes . object ) . isRequired ,
377398
378- // Callback for move operation.
379- // Called as moveNode({ node, path, parentPath, minimumTreeIndex })
380- moveNode : PropTypes . func ,
381-
382399 // Style applied to the container wrapping the tree (style defaults to {height: '100%'})
383400 style : PropTypes . object ,
401+
402+ // Class name for the container wrapping the tree
384403 className : PropTypes . string ,
385404
386405 // Style applied to the inner, scrollable container (for padding, etc.)
387406 innerStyle : PropTypes . object ,
388407
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`
390411 rowHeight : PropTypes . oneOfType ( [ PropTypes . number , PropTypes . func ] ) ,
391412
413+ // The width of the blocks containing the lines representing the structure of the tree.
392414 scaffoldBlockPxWidth : PropTypes . number ,
393415
416+ // Maximum depth nodes can be inserted at. Defaults to infinite.
394417 maxDepth : PropTypes . number ,
395418
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.
400433 searchFinishCallback : PropTypes . func , // eslint-disable-line react/no-unused-prop-types
401434
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.
402443 nodeContentRenderer : PropTypes . any ,
403- generateNodeProps : PropTypes . func ,
404444
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 ,
408460} ;
409461
410462ReactSortableTree . defaultProps = {
463+ getNodeKey : defaultGetNodeKey ,
464+ nodeContentRenderer : NodeRendererDefault ,
411465 rowHeight : 62 ,
466+ scaffoldBlockPxWidth : 44 ,
412467 style : { } ,
413468 innerStyle : { } ,
414- scaffoldBlockPxWidth : 44 ,
415- loadCollapsedLazyChildren : false ,
416469 searchQuery : null ,
417470} ;
418471
0 commit comments