Skip to content

Commit 14fcc66

Browse files
committed
Merge remote-tracking branch 'FredyC/iteratable-support'
2 parents 644a202 + fff7e39 commit 14fcc66

File tree

3 files changed

+151
-0
lines changed

3 files changed

+151
-0
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
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 entry in iterable. 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 entries" 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 (Number.isSafeInteger(data.size)) {
91+
count = data.size;
92+
} else {
93+
for (const entry of data) { // eslint-disable-line no-unused-vars
94+
count += 1;
95+
}
96+
}
97+
this.itemString = count + ' entr' + (count !== 1 ? 'ies' : 'y');
98+
}
99+
return this.itemString;
100+
}
101+
102+
render() {
103+
const childNodes = this.getChildNodes();
104+
const childListStyle = {
105+
padding: 0,
106+
margin: 0,
107+
listStyle: 'none',
108+
display: (this.state.expanded) ? 'block' : 'none'
109+
};
110+
let containerStyle;
111+
let spanStyle = {
112+
...styles.span,
113+
color: this.props.theme.base0E
114+
};
115+
containerStyle = {
116+
...styles.base
117+
};
118+
if (this.state.expanded) {
119+
spanStyle = {
120+
...spanStyle,
121+
color: this.props.theme.base03
122+
};
123+
}
124+
return (
125+
<li style={containerStyle}>
126+
<JSONArrow theme={this.props.theme} open={this.state.expanded} onClick={::this.handleClick}/>
127+
<label style={{
128+
...styles.label,
129+
color: this.props.theme.base0D
130+
}} onClick={::this.handleClick}>
131+
{this.props.keyName}:
132+
</label>
133+
<span style={spanStyle} onClick={::this.handleClick}>
134+
<span style={styles.spanType}>()</span>
135+
{this.getItemString()}
136+
</span>
137+
<ol style={childListStyle}>
138+
{childNodes}
139+
</ol>
140+
</li>
141+
);
142+
}
143+
}

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 & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
export default function(obj) {
2+
if (obj !== null && typeof obj === 'object' && !Array.isArray(obj) &&
3+
typeof obj[Symbol.iterator] === 'function'
4+
) {
5+
return 'Iterable';
6+
}
27
return Object.prototype.toString.call(obj).slice(8, -1);
38
}

0 commit comments

Comments
 (0)