@@ -27,185 +27,186 @@ npm install --save react-infinite-tree
27
27
28
28
## Example
29
29
30
- #### Tree.jsx
30
+ ### Tree
31
+
31
32
``` jsx
32
- < InfiniteTree
33
- ref= {node => {
34
- this .tree = node ? node .tree : null ;
35
- }}
36
- style= {{
37
- border: ' 1px solid #ccc'
38
- }}
39
- data= {this .data }
40
- width= " 100%"
41
- height= {400 }
42
- rowHeight= {30 }
43
- autoOpen= {true }
44
- loadNodes= {(parentNode , done ) => {
45
- const suffix = parentNode .id .replace (/ (\w )+ / , ' ' );
46
- const nodes = [
47
- {
48
- id: ' node1' + suffix,
49
- name: ' Node 1'
50
- },
51
- {
52
- id: ' node2' + suffix,
53
- name: ' Node 2'
54
- }
55
- ];
56
- setTimeout (() => {
57
- done (null , nodes);
58
- }, 1000 );
59
- }}
60
- selectable= {true } // Defaults to true
61
- shouldSelectNode= {(node ) => { // Defaults to null
62
- if (! node || (node === this .tree .getSelectedNode ())) {
63
- return false ; // Prevent from deselecting the current node
64
- }
65
- return true ;
66
- }}
67
- onKeyDown= {(event ) => {
68
- const target = event .target || event .srcElement ; // IE8
69
- console .log (' onKeyDown' , target);
70
- event .preventDefault ();
71
-
72
- const node = this .tree .getSelectedNode ();
73
- const nodeIndex = this .tree .getSelectedIndex ();
74
-
75
- if (event .keyCode === 37 ) { // Left
76
- this .tree .closeNode (node);
77
- } else if (event .keyCode === 38 ) { // Up
78
- const prevNode = this .tree .nodes [nodeIndex - 1 ] || node;
79
- this .tree .selectNode (prevNode);
80
- } else if (event .keyCode === 39 ) { // Right
81
- this .tree .openNode (node);
82
- } else if (event .keyCode === 40 ) { // Down
83
- const nextNode = this .tree .nodes [nodeIndex + 1 ] || node;
84
- this .tree .selectNode (nextNode);
33
+ import PropTypes from ' prop-types' ;
34
+ import React , { PureComponent } from ' react' ;
35
+ import InfiniteTree from ' ../src' ;
36
+ import TreeNode from ' ./components/TreeNode' ;
37
+ import Toggler from ' ./components/Toggler' ;
38
+ import Icon from ' ./components/Icon' ;
39
+ import Clickable from ' ./components/Clickable' ;
40
+ import Text from ' ./components/Text' ;
41
+ import Label from ' ./components/Label' ;
42
+ import Loading from ' ./components/Loading' ;
43
+ import { generate } from ' ./tree-generator' ;
44
+
45
+ const renderTreeNode = ({ node, tree, toggleState }) => (
46
+ < TreeNode
47
+ selected= {node .state .selected }
48
+ depth= {node .state .depth }
49
+ onClick= {event => {
50
+ tree .selectNode (node);
51
+ }}
52
+ >
53
+ < Toggler
54
+ state= {toggleState}
55
+ onClick= {() => {
56
+ if (toggleState === ' closed' ) {
57
+ tree .openNode (node);
58
+ } else if (toggleState === ' opened' ) {
59
+ tree .closeNode (node);
60
+ }
61
+ }}
62
+ / >
63
+ < Clickable>
64
+ < Icon state= {toggleState} / >
65
+ < Text > {node .name }< / Text >
66
+ < / Clickable>
67
+ {(node .loadOnDemand && node .children .length === 0 && ! node .state .loading ) &&
68
+ < i className= " fa fa-fw fa-ellipsis-v" / >
85
69
}
86
- }}
87
- >
88
- {({ node, tree }) => {
89
- const { id , name , loadOnDemand = false , children, state, props = {} } = node;
90
- const { depth , open , path , total , loading = false , selected = false } = state;
91
- const childrenLength = Object .keys (children).length ;
92
- const more = node .hasChildren ();
93
-
94
- let togglerState = ' ' ;
95
- if ((! more && loadOnDemand) || (more && ! open)) {
96
- togglerState = ' collapsed' ;
97
- }
98
- if (more && open) {
99
- togglerState = ' expanded' ;
100
- }
70
+ {node .state .loading && < Loading / > }
71
+ < Label style= {{ position: ' absolute' , right: 5 , top: 6 }}>
72
+ {node .children .length }
73
+ < / Label>
74
+ < / TreeNode>
75
+ );
101
76
102
- let iconState = ' ' ;
103
- if (more && open) {
104
- iconState = ' folder-open' ;
105
- }
106
- if (more && ! open) {
107
- iconState = ' folder' ;
108
- }
109
- if (! more) {
110
- iconState = ' file' ;
111
- }
77
+ class Tree extends PureComponent {
78
+ tree = null ;
79
+ data = generate (1000 );
112
80
113
- return (
114
- < TreeNode
115
- selected= {selected}
116
- depth= {depth}
117
- onClick= {event => {
118
- tree .selectNode (node);
119
- }}
120
- >
121
- < Toggler
122
- state= {togglerState}
123
- onClick= {() => {
124
- if (togglerState === ' collapsed' ) {
125
- tree .openNode (node);
126
- } else if (togglerState === ' expanded' ) {
127
- tree .closeNode (node);
81
+ componentDidMount () {
82
+ // Select the first node
83
+ this .tree .selectNode (this .tree .getChildNodes ()[0 ]);
84
+ }
85
+ render () {
86
+ return (
87
+ < InfiniteTree
88
+ ref= {node => {
89
+ this .tree = node ? node .tree : null ;
90
+ }}
91
+ style= {{
92
+ border: ' 1px solid #ccc'
93
+ }}
94
+ autoOpen
95
+ selectable
96
+ tabIndex= {0 }
97
+ data= {this .data }
98
+ width= " 100%"
99
+ height= {400 }
100
+ rowHeight= {30 }
101
+ rowRenderer= {({ node, tree }) => {
102
+ const hasChildren = node .hasChildren ();
103
+ let toggleState = ' ' ;
104
+ if ((! hasChildren && node .loadOnDemand ) || (hasChildren && ! node .state .open )) {
105
+ toggleState = ' closed' ;
106
+ }
107
+ if (hasChildren && node .state .open ) {
108
+ toggleState = ' opened' ;
109
+ }
110
+ return renderTreeNode ({ node, tree, toggleState });
111
+ }}
112
+ loadNodes= {(parentNode , done ) => {
113
+ const suffix = parentNode .id .replace (/ (\w )+ / , ' ' );
114
+ const nodes = [
115
+ {
116
+ id: ' node1' + suffix,
117
+ name: ' Node 1'
118
+ },
119
+ {
120
+ id: ' node2' + suffix,
121
+ name: ' Node 2'
122
+ }
123
+ ];
124
+ setTimeout (() => {
125
+ done (null , nodes);
126
+ }, 1000 );
127
+ }}
128
+ shouldSelectNode= {(node ) => { // Defaults to null
129
+ if (! node || (node === this .tree .getSelectedNode ())) {
130
+ return false ; // Prevent from deselecting the current node
128
131
}
132
+ return true ;
129
133
}}
134
+ onKeyUp= {(event ) => {
135
+ console .log (' onKeyUp' , event .target );
136
+ }}
137
+ onKeyDown= {(event ) => {
138
+ console .log (' onKeyDown' , event .target );
139
+ event .preventDefault ();
140
+ const node = this .tree .getSelectedNode ();
141
+ const nodeIndex = this .tree .getSelectedIndex ();
142
+ if (event .keyCode === 37 ) { // Left
143
+ this .tree .closeNode (node);
144
+ } else if (event .keyCode === 38 ) { // Up
145
+ const prevNode = this .tree .nodes [nodeIndex - 1 ] || node;
146
+ this .tree .selectNode (prevNode);
147
+ } else if (event .keyCode === 39 ) { // Right
148
+ this .tree .openNode (node);
149
+ } else if (event .keyCode === 40 ) { // Down
150
+ const nextNode = this .tree .nodes [nodeIndex + 1 ] || node;
151
+ this .tree .selectNode (nextNode);
152
+ }
153
+ }}
154
+ onScroll= {(scrollOffset , event ) => {}}
155
+ onContentWillUpdate= {() => {}}
156
+ onContentDidUpdate= {() => {}}
157
+ onOpenNode= {(node ) => {}}
158
+ onCloseNode= {(node ) => {}}
159
+ onSelectNode= {(node ) => {}}
160
+ onWillOpenNode= {(node ) => {}}
161
+ onWillCloseNode= {(node ) => {}}
162
+ onWillSelectNode= {(node ) => {}}
130
163
/ >
131
- < PointerCursor>
132
- < Icon state= {iconState} / >
133
- < Text > {name}< / Text >
134
- < / PointerCursor>
135
- < / TreeNode>
136
- );
137
- }}
138
- < / InfiniteTree>
139
- ```
140
-
141
- #### render.jsx
142
- ``` jsx
143
- import React from ' react' ;
144
- import styled from ' styled-components' ;
145
-
146
- const rowHeight = 30 ;
147
-
148
- export const TreeNode = styled .div `
149
- cursor: default;
150
- position: relative;
151
- line-height: ${ rowHeight - 2 } px;
152
- background: ${ props => props .selected ? ' #deecfd' : ' transparent' } ;
153
- border: ${ props => props .selected ? ' 1px solid #06c' : ' 1px solid transparent' } ;
154
- &:hover {
155
- background: #f2fdff;
164
+ );
156
165
}
157
- padding-left: ${ props => props .depth * 18 } px;
158
- ` ;
159
-
160
- export const Toggler = styled (({ state, ... props }) => (
161
- < a {... props}>
162
- {(state === ' expanded' ) &&
163
- < i className= " fa fa-fw fa-chevron-right" / >
164
- }
165
- {(state === ' collapsed' ) &&
166
- < i className= " fa fa-fw fa-chevron-down" / >
167
- }
168
- < / a>
169
- ))`
170
- color: #333;
171
- display: inine-block;
172
- text-align: center;
173
- margin-right: 2px;
174
- ` ;
175
-
176
- export const Icon = ({ state, ... props }) => (
177
- < span {... props}>
178
- {(state === ' folder-open' ) &&
179
- < i className= " fa fa-fw fa-folder-open-o" / >
180
- }
181
- {(state === ' folder' ) &&
182
- < i className= " fa fa-fw fa-folder-o" / >
183
- }
184
- {(state === ' file' ) &&
185
- < i className= " fa fa-fw fa-file-o" / >
186
- }
187
- < / span>
188
- );
189
-
190
- export const PointerCursor = styled .div `
191
- display: inline-block;
192
- cursor: pointer;
193
- ` ;
166
+ }
194
167
195
- export const Text = styled .span `
196
- margin-left: 0 2px;
197
- user-select: none;
198
- ` ;
168
+ export default Tree ;
199
169
```
200
170
201
- ## API Documentation
202
-
203
- Check out API documentation at [ infinite-tree] ( https://github.com/cheton/infinite-tree/wiki ) :
204
-
205
- * [ Options] ( https://github.com/cheton/react-infinite-tree/wiki/Options )
206
- * [ Functions: Tree] ( https://github.com/cheton/react-infinite-tree/wiki/Functions:-Tree )
207
- * [ Functions: Node] ( https://github.com/cheton/react-infinite-tree/wiki/Functions:-Node )
208
- * [ Events] ( https://github.com/cheton/react-infinite-tree/wiki/Events )
171
+ ### Components
172
+
173
+ https://github.com/cheton/react-infinite-tree/tree/master/examples/components
174
+
175
+ # API
176
+
177
+ ### Properties
178
+
179
+ Name | Type | Default | Description
180
+ :--- | :--- | :------ | :----------
181
+ autoOpen | Boolean | false | Whether to open all nodes when tree is loaded.
182
+ selectable | Boolean | true | Whether or not a node is selectable in the tree.
183
+ tabIndex | Number | 0 | Specifies the tab order to make tree focusable.
184
+ data | Array or Object | [ ] | Tree data structure, or a collection of tree data structures.
185
+ width \* | Number or String | '100%' | Width of the tree.
186
+ height \* | Number or String | | Height of the tree.
187
+ rowHeight \* | Number, Array, or Function(index: Number): Number | | Either a fixed height, an array containing the heights of all the rows, or a function that returns the height of a row given its index.
188
+ rowRenderer | Function({ node: Node, tree: Tree }): React Node | | A row renderer for rendering a tree node.
189
+ loadNodes | Function(parentNode: Node, done: Function) | | Loads nodes on demand.
190
+ shouldSelectNode | Function(node: Node): Boolean | | Provides a function to determine if a node can be selected or deselected. The function must return ` true ` or ` false ` . This function will not take effect if ` selectable ` is not ` true ` .
191
+ scrollOffset | Number | | Controls the scroll offset.
192
+ scrollToIndex | Number | | Node index to scroll to.
193
+ onScroll | Function(scrollTop: Number, event: React.UIEvent) | | Callback invoked whenever the scroll offset changes.
194
+ onContentWillUpdate | Function() | | Callback invoked before updating the tree.
195
+ onContentDidUpdate | Function() | | Callback invoked when the tree is updated.
196
+ onOpenNode | Function(node: Node) | | Callback invoked when a node is opened.
197
+ onCloseNode | Function(node: Node) | | Callback invoked when a node is closed.
198
+ onSelectNode | Function(node: Node) | | Callback invoked when a node is selected or deselected.
199
+ onWillOpenNode | Function(node: Node) | | Callback invoked before opening a node.
200
+ onWillCloseNode | Function(node: Node) | | Callback invoked before closing a node.
201
+ onWillSelectNode | Function(node: Node) | | Callback invoked before selecting or deselecting a node.
202
+
203
+ ### Tree Methods
204
+
205
+ https://github.com/cheton/react-infinite-tree/wiki/Functions:-Tree
206
+
207
+ ### Node Methods
208
+
209
+ https://github.com/cheton/react-infinite-tree/wiki/Functions:-Node
209
210
210
211
## License
211
212
0 commit comments