Skip to content

Commit 3e82031

Browse files
committed
Continue reworking InfiniteTree component
1 parent 5d62e22 commit 3e82031

File tree

15 files changed

+334
-500
lines changed

15 files changed

+334
-500
lines changed

examples/Tree.jsx

Lines changed: 100 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,73 @@
1+
import Dropdown, { MenuItem } from '@trendmicro/react-dropdown';
12
import PropTypes from 'prop-types';
23
import React, { PureComponent } from 'react';
3-
import InfiniteTree from '../src/InfiniteTree';
4-
import render from './render';
5-
import '../src/index.styl';
4+
import InfiniteTree from '../src';
5+
import TreeNode from './components/TreeNode';
6+
import Toggler from './components/Toggler';
7+
import Icon from './components/Icon';
8+
import Clickable from './components/Clickable';
9+
import Text from './components/Text';
10+
import Label from './components/Label';
11+
import Loading from './components/Loading';
12+
import { generate } from './tree-generator';
613

7-
const generateData = () => {
8-
const data = [];
9-
const source = '{"id":"<root>","name":"<root>","props":{"droppable":true},"children":[{"id":"alpha","name":"Alpha","props":{"droppable":true}},{"id":"bravo","name":"Bravo","props":{"droppable":true},"children":[{"id":"charlie","name":"Charlie","props":{"droppable":true},"children":[{"id":"delta","name":"Delta","props":{"droppable":true},"children":[{"id":"echo","name":"Echo","props":{"droppable":true}},{"id":"foxtrot","name":"Foxtrot","props":{"droppable":true}}]},{"id":"golf","name":"Golf","props":{"droppable":true}}]},{"id":"hotel","name":"Hotel","props":{"droppable":true},"children":[{"id":"india","name":"India","props":{"droppable":true},"children":[{"id":"juliet","name":"Juliet","props":{"droppable":true}}]}]},{"id":"kilo","name":"(Load On Demand) Kilo","loadOnDemand":true,"props":{"droppable":true}}]}]}';
10-
for (let i = 0; i < 1000; ++i) {
11-
data.push(JSON.parse(source.replace(/"(id|name)":"([^"]*)"/g, '"$1": "$2.' + i + '"')));
12-
}
13-
return data;
14-
};
14+
const renderTreeNode = ({ node, tree, toggleState }) => (
15+
<TreeNode
16+
selected={node.state.selected}
17+
depth={node.state.depth}
18+
onClick={event => {
19+
tree.selectNode(node);
20+
}}
21+
>
22+
<Toggler
23+
state={toggleState}
24+
onClick={() => {
25+
if (toggleState === 'closed') {
26+
tree.openNode(node);
27+
} else if (toggleState === 'opened') {
28+
tree.closeNode(node);
29+
}
30+
}}
31+
/>
32+
<Clickable>
33+
<Icon state={toggleState} />
34+
<Text>{node.name}</Text>
35+
</Clickable>
36+
{(node.loadOnDemand && node.children.length === 0 && !node.state.loading) &&
37+
<i className="fa fa-fw fa-ellipsis-v" />
38+
}
39+
{node.state.loading && <Loading />}
40+
<Label style={{ position: 'absolute', right: 5, top: 6 }}>
41+
{node.children.length}
42+
</Label>
43+
<Dropdown
44+
style={{ position: 'absolute', right: 20, top: 4 }}
45+
pullRight
46+
>
47+
<Dropdown.Toggle
48+
noCaret
49+
btnSize="xs"
50+
btnStyle="link"
51+
style={{ padding: 0 }}
52+
>
53+
<i className="dropdown fa fa-fw fa-cog" />
54+
</Dropdown.Toggle>
55+
<Dropdown.Menu>
56+
<MenuItem>{node.name}</MenuItem>
57+
</Dropdown.Menu>
58+
</Dropdown>
59+
</TreeNode>
60+
);
1561

1662
class Tree extends PureComponent {
1763
static propTypes = {
1864
onUpdate: PropTypes.func
1965
};
2066

2167
tree = null;
22-
data = generateData();
68+
data = generate(1000);
2369

2470
componentDidMount() {
25-
//this.tree.loadData(data);
26-
2771
// Select the first node
2872
this.tree.selectNode(this.tree.getChildNodes()[0]);
2973
}
@@ -36,11 +80,26 @@ class Tree extends PureComponent {
3680
style={{
3781
border: '1px solid #ccc'
3882
}}
83+
autoOpen
84+
selectable
85+
tabIndex={0}
3986
data={this.data}
4087
width="100%"
4188
height={400}
4289
rowHeight={30}
43-
autoOpen={true}
90+
rowRenderer={({ node, tree }) => {
91+
const hasChildren = node.hasChildren();
92+
93+
let toggleState = '';
94+
if ((!hasChildren && node.loadOnDemand) || (hasChildren && !node.state.open)) {
95+
toggleState = 'closed';
96+
}
97+
if (hasChildren && node.state.open) {
98+
toggleState = 'opened';
99+
}
100+
101+
return renderTreeNode({ node, tree, toggleState });
102+
}}
44103
loadNodes={(parentNode, done) => {
45104
const suffix = parentNode.id.replace(/(\w)+/, '');
46105
const nodes = [
@@ -57,16 +116,18 @@ class Tree extends PureComponent {
57116
done(null, nodes);
58117
}, 1000);
59118
}}
60-
selectable={true} // Defaults to true
61119
shouldSelectNode={(node) => { // Defaults to null
62120
if (!node || (node === this.tree.getSelectedNode())) {
63121
return false; // Prevent from deselecting the current node
64122
}
65123
return true;
66124
}}
125+
onKeyUp={(event) => {
126+
console.log('onKeyUp', event.target);
127+
}}
67128
onKeyDown={(event) => {
68-
const target = event.target || event.srcElement; // IE8
69-
console.log('onKeyDown', target);
129+
console.log('onKeyDown', event.target);
130+
70131
event.preventDefault();
71132

72133
const node = this.tree.getSelectedNode();
@@ -88,16 +149,32 @@ class Tree extends PureComponent {
88149
console.log('onContentWillUpdate');
89150
}}
90151
onContentDidUpdate={() => {
152+
console.log('onContentDidUpdate');
91153
this.props.onUpdate(this.tree.getSelectedNode());
92154
}}
155+
onOpenNode={(node) => {
156+
console.log('onOpenNode:', node);
157+
}}
158+
onCloseNode={(node) => {
159+
console.log('onCloseNode:', node);
160+
}}
93161
onSelectNode={(node) => {
162+
console.log('onSelectNode:', node);
94163
this.props.onUpdate(node);
95164
}}
96-
>
97-
{({ node, tree }) => {
98-
return render({ node, tree });
99-
}}
100-
</InfiniteTree>
165+
onWillOpenNode={(node) => {
166+
console.log('onWillOpenNode:', node);
167+
}}
168+
onWillCloseNode={(node) => {
169+
console.log('onWillCloseNode:', node);
170+
}}
171+
onWillSelectNode={(node) => {
172+
console.log('onWillSelectNode:', node);
173+
}}
174+
onScroll={(scrollOffset, event) => {
175+
console.log('## onScroll', scrollOffset, event);
176+
}}
177+
/>
101178
);
102179
}
103180
}

examples/animation.styl

Lines changed: 0 additions & 22 deletions
This file was deleted.

examples/components/Clickable.jsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import React from 'react';
2+
import styled from 'styled-components';
3+
4+
const Clickable = styled.div`
5+
display: inline-block;
6+
cursor: pointer;
7+
`;
8+
9+
export default Clickable;

examples/components/Icon.jsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from 'react';
2+
3+
const Icon = ({ state, ...props }) => (
4+
<span {...props}>
5+
{{
6+
'opened': (<i className="fa fa-fw fa-folder-open-o" />),
7+
'closed': (<i className="fa fa-fw fa-folder-o" />),
8+
}[state] || (<i className="fa fa-fw fa-file-o" />)}
9+
</span>
10+
);
11+
12+
export default Icon;

examples/components/Label.jsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react';
2+
import styled from 'styled-components';
3+
4+
const Label = styled.span`
5+
display: inline;
6+
padding: .2em .6em .3em;
7+
font-size: 75%;
8+
font-weight: 700;
9+
line-height: 1;
10+
color :#fff;
11+
text-align: center;
12+
white-space: nowrap;
13+
vertical-align: baseline;
14+
border-radius: .25em;
15+
background-color: #337ab7;
16+
user-select: none;
17+
`;
18+
19+
export default Label;

examples/components/Loading.jsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import React from 'react';
2+
3+
const Loading = () => (
4+
<i className="fa fa-fw fa-spin fa-spinner" />
5+
);
6+
7+
export default Loading;

examples/components/Text.jsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import React from 'react';
2+
import styled from 'styled-components';
3+
4+
const Text = styled.span`
5+
margin-left: 0 2px;
6+
user-select: none;
7+
`;
8+
9+
export default Text;

examples/components/Toggler.jsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React from 'react';
2+
import styled from 'styled-components';
3+
4+
const Toggler = styled(({ state, ...props }) => (
5+
<a {...props}>
6+
{(state === 'closed') &&
7+
<i className="fa fa-fw fa-chevron-right" />
8+
}
9+
{(state === 'opened') &&
10+
<i className="fa fa-fw fa-chevron-down" />
11+
}
12+
</a>
13+
))`
14+
color: #333;
15+
display: inline-block;
16+
text-align: center;
17+
margin-right: 2px;
18+
`;
19+
20+
export default Toggler;

examples/components/TreeNode.jsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import React from 'react';
2+
import styled from 'styled-components';
3+
4+
const defaultRowHeight = 30;
5+
6+
const TreeNode = styled.div`
7+
cursor: default;
8+
position: relative;
9+
line-height: ${({ rowHeight = defaultRowHeight }) => rowHeight - 2}px;
10+
background: ${props => props.selected ? '#deecfd' : 'transparent'};
11+
border: ${props => props.selected ? '1px solid #06c' : '1px solid transparent'};
12+
padding-left: ${props => props.depth * 18}px;
13+
14+
.dropdown {
15+
visibility: hidden;
16+
}
17+
18+
&:hover {
19+
background: #f2fdff;
20+
21+
.dropdown {
22+
visibility: inherit;
23+
}
24+
}
25+
`;
26+
27+
export default TreeNode;

0 commit comments

Comments
 (0)