Skip to content

Commit 56de89c

Browse files
committed
support for state with iterable structures
1 parent b09d171 commit 56de89c

File tree

3 files changed

+149
-1
lines changed

3 files changed

+149
-1
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import React from 'react';
2+
import reactMixin from 'react-mixin';
3+
import { ExpandedStateHandlerMixin } from './mixins';
4+
import JSONArrow from './JSONArrow';
5+
import grabNode from './grab-node';
6+
7+
const styles = {
8+
base: {
9+
position: 'relative',
10+
paddingTop: 3,
11+
paddingBottom: 3,
12+
paddingRight: 0,
13+
marginLeft: 14
14+
},
15+
label: {
16+
margin: 0,
17+
padding: 0,
18+
display: 'inline-block'
19+
},
20+
span: {
21+
cursor: 'default'
22+
},
23+
spanType: {
24+
marginLeft: 5,
25+
marginRight: 5
26+
}
27+
};
28+
29+
@reactMixin.decorate(ExpandedStateHandlerMixin)
30+
export default class JSONIterableNode extends React.Component {
31+
defaultProps = {
32+
data: [],
33+
initialExpanded: false
34+
};
35+
36+
// flag to see if we still need to render our child nodes
37+
needsChildNodes = true;
38+
39+
// cache store for our child nodes
40+
renderedChildren = [];
41+
42+
// cache store for the number of items string we display
43+
itemString = false;
44+
45+
constructor(props) {
46+
super(props);
47+
this.state = {
48+
expanded: this.props.initialExpanded,
49+
createdChildNodes: false
50+
};
51+
}
52+
53+
// Returns the child nodes for each element in the array. If we have
54+
// generated them previously, we return from cache, otherwise we create
55+
// them.
56+
getChildNodes() {
57+
if (this.state.expanded && this.needsChildNodes) {
58+
let childNodes = [];
59+
for (const entry of this.props.data) {
60+
let key = null;
61+
let value = null;
62+
if (Array.isArray(entry)) {
63+
[key, value] = entry;
64+
} else {
65+
key = childNodes.length;
66+
value = entry;
67+
}
68+
69+
let prevData;
70+
if (typeof this.props.previousData !== 'undefined') {
71+
prevData = this.props.previousData[key];
72+
}
73+
const node = grabNode(key, value, prevData, this.props.theme);
74+
if (node !== false) {
75+
childNodes.push(node);
76+
}
77+
}
78+
this.needsChildNodes = false;
79+
this.renderedChildren = childNodes;
80+
}
81+
return this.renderedChildren;
82+
}
83+
84+
// Returns the "n Items" string for this node, generating and
85+
// caching it if it hasn't been created yet.
86+
getItemString() {
87+
if (!this.itemString) {
88+
const { data } = this.props;
89+
let count = 0;
90+
if (typeof data.count === 'function') {
91+
count = data.count();
92+
} else {
93+
count = data.length;
94+
}
95+
this.itemString = count + ' entr' + (count !== 1 ? 'ies' : 'y');
96+
}
97+
return this.itemString;
98+
}
99+
100+
render() {
101+
const childNodes = this.getChildNodes();
102+
const childListStyle = {
103+
padding: 0,
104+
margin: 0,
105+
listStyle: 'none',
106+
display: (this.state.expanded) ? 'block' : 'none'
107+
};
108+
let containerStyle;
109+
let spanStyle = {
110+
...styles.span,
111+
color: this.props.theme.base0E
112+
};
113+
containerStyle = {
114+
...styles.base
115+
};
116+
if (this.state.expanded) {
117+
spanStyle = {
118+
...spanStyle,
119+
color: this.props.theme.base03
120+
};
121+
}
122+
return (
123+
<li style={containerStyle}>
124+
<JSONArrow theme={this.props.theme} open={this.state.expanded} onClick={::this.handleClick}/>
125+
<label style={{
126+
...styles.label,
127+
color: this.props.theme.base0D
128+
}} onClick={::this.handleClick}>
129+
{this.props.keyName}:
130+
</label>
131+
<span style={spanStyle} onClick={::this.handleClick}>
132+
<span style={styles.spanType}>()</span>
133+
{this.getItemString()}
134+
</span>
135+
<ol style={childListStyle}>
136+
{childNodes}
137+
</ol>
138+
</li>
139+
);
140+
}
141+
}

src/react/JSONTree/grab-node.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22
import objType from './obj-type';
33
import JSONObjectNode from './JSONObjectNode';
44
import JSONArrayNode from './JSONArrayNode';
5+
import JSONIterableNode from './JSONIterableNode';
56
import JSONStringNode from './JSONStringNode';
67
import JSONNumberNode from './JSONNumberNode';
78
import JSONBooleanNode from './JSONBooleanNode';
@@ -13,6 +14,8 @@ export default function(key, value, prevValue, theme) {
1314
return <JSONObjectNode data={value} previousData={prevValue} theme={theme} keyName={key} key={key} />;
1415
} else if (nodeType === 'Array') {
1516
return <JSONArrayNode data={value} previousData={prevValue} theme={theme} keyName={key} key={key} />;
17+
} else if (nodeType === 'Iterable') {
18+
return <JSONIterableNode data={value} previousData={prevValue} theme={theme} keyName={key} key={key} />;
1619
} else if (nodeType === 'String') {
1720
return <JSONStringNode keyName={key} previousValue={prevValue} theme={theme} value={value} key={key} />;
1821
} else if (nodeType === 'Number') {

src/react/JSONTree/obj-type.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
export default function(obj) {
2-
return Object.prototype.toString.call(obj).slice(8, -1);
2+
const type = Object.prototype.toString.call(obj).slice(8, -1);
3+
if (type === 'Object' && typeof obj[Symbol.iterator] === 'function') {
4+
return 'Iterable';
5+
}
6+
return type;
37
}

0 commit comments

Comments
 (0)