From 56de89c4409cd879f7c2ab540af18af4ec1462ca Mon Sep 17 00:00:00 2001 From: FredyC Date: Thu, 20 Aug 2015 10:16:39 +0200 Subject: [PATCH 1/5] support for state with iterable structures --- src/react/JSONTree/JSONIterableNode.js | 141 +++++++++++++++++++++++++ src/react/JSONTree/grab-node.js | 3 + src/react/JSONTree/obj-type.js | 6 +- 3 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 src/react/JSONTree/JSONIterableNode.js diff --git a/src/react/JSONTree/JSONIterableNode.js b/src/react/JSONTree/JSONIterableNode.js new file mode 100644 index 0000000000..3181adb252 --- /dev/null +++ b/src/react/JSONTree/JSONIterableNode.js @@ -0,0 +1,141 @@ +import React from 'react'; +import reactMixin from 'react-mixin'; +import { ExpandedStateHandlerMixin } from './mixins'; +import JSONArrow from './JSONArrow'; +import grabNode from './grab-node'; + +const styles = { + base: { + position: 'relative', + paddingTop: 3, + paddingBottom: 3, + paddingRight: 0, + marginLeft: 14 + }, + label: { + margin: 0, + padding: 0, + display: 'inline-block' + }, + span: { + cursor: 'default' + }, + spanType: { + marginLeft: 5, + marginRight: 5 + } +}; + +@reactMixin.decorate(ExpandedStateHandlerMixin) +export default class JSONIterableNode extends React.Component { + defaultProps = { + data: [], + initialExpanded: false + }; + + // flag to see if we still need to render our child nodes + needsChildNodes = true; + + // cache store for our child nodes + renderedChildren = []; + + // cache store for the number of items string we display + itemString = false; + + constructor(props) { + super(props); + this.state = { + expanded: this.props.initialExpanded, + createdChildNodes: false + }; + } + + // Returns the child nodes for each element in the array. If we have + // generated them previously, we return from cache, otherwise we create + // them. + getChildNodes() { + if (this.state.expanded && this.needsChildNodes) { + let childNodes = []; + for (const entry of this.props.data) { + let key = null; + let value = null; + if (Array.isArray(entry)) { + [key, value] = entry; + } else { + key = childNodes.length; + value = entry; + } + + let prevData; + if (typeof this.props.previousData !== 'undefined') { + prevData = this.props.previousData[key]; + } + const node = grabNode(key, value, prevData, this.props.theme); + if (node !== false) { + childNodes.push(node); + } + } + this.needsChildNodes = false; + this.renderedChildren = childNodes; + } + return this.renderedChildren; + } + + // Returns the "n Items" string for this node, generating and + // caching it if it hasn't been created yet. + getItemString() { + if (!this.itemString) { + const { data } = this.props; + let count = 0; + if (typeof data.count === 'function') { + count = data.count(); + } else { + count = data.length; + } + this.itemString = count + ' entr' + (count !== 1 ? 'ies' : 'y'); + } + return this.itemString; + } + + render() { + const childNodes = this.getChildNodes(); + const childListStyle = { + padding: 0, + margin: 0, + listStyle: 'none', + display: (this.state.expanded) ? 'block' : 'none' + }; + let containerStyle; + let spanStyle = { + ...styles.span, + color: this.props.theme.base0E + }; + containerStyle = { + ...styles.base + }; + if (this.state.expanded) { + spanStyle = { + ...spanStyle, + color: this.props.theme.base03 + }; + } + return ( +
  • + + + + () + {this.getItemString()} + +
      + {childNodes} +
    +
  • + ); + } +} diff --git a/src/react/JSONTree/grab-node.js b/src/react/JSONTree/grab-node.js index d3851433e3..f94012a848 100644 --- a/src/react/JSONTree/grab-node.js +++ b/src/react/JSONTree/grab-node.js @@ -2,6 +2,7 @@ import React from 'react'; import objType from './obj-type'; import JSONObjectNode from './JSONObjectNode'; import JSONArrayNode from './JSONArrayNode'; +import JSONIterableNode from './JSONIterableNode'; import JSONStringNode from './JSONStringNode'; import JSONNumberNode from './JSONNumberNode'; import JSONBooleanNode from './JSONBooleanNode'; @@ -13,6 +14,8 @@ export default function(key, value, prevValue, theme) { return ; } else if (nodeType === 'Array') { return ; + } else if (nodeType === 'Iterable') { + return ; } else if (nodeType === 'String') { return ; } else if (nodeType === 'Number') { diff --git a/src/react/JSONTree/obj-type.js b/src/react/JSONTree/obj-type.js index e0b964c120..095ee2d7f8 100644 --- a/src/react/JSONTree/obj-type.js +++ b/src/react/JSONTree/obj-type.js @@ -1,3 +1,7 @@ export default function(obj) { - return Object.prototype.toString.call(obj).slice(8, -1); + const type = Object.prototype.toString.call(obj).slice(8, -1); + if (type === 'Object' && typeof obj[Symbol.iterator] === 'function') { + return 'Iterable'; + } + return type; } From c76f7558637ab53cdca1820d8f2e485a28be15ad Mon Sep 17 00:00:00 2001 From: FredyC Date: Sat, 22 Aug 2015 06:09:42 +0200 Subject: [PATCH 2/5] safer type checking for iterable structures --- src/react/JSONTree/obj-type.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/react/JSONTree/obj-type.js b/src/react/JSONTree/obj-type.js index 095ee2d7f8..40dfd8d1a0 100644 --- a/src/react/JSONTree/obj-type.js +++ b/src/react/JSONTree/obj-type.js @@ -1,7 +1,6 @@ export default function(obj) { - const type = Object.prototype.toString.call(obj).slice(8, -1); - if (type === 'Object' && typeof obj[Symbol.iterator] === 'function') { + if (obj !== null && typeof obj === 'object' && typeof obj[Symbol.iterator] === 'function') { return 'Iterable'; } - return type; + return Object.prototype.toString.call(obj).slice(8, -1); } From 04f15f6d4de80f09544048c9bc606853309293c2 Mon Sep 17 00:00:00 2001 From: FredyC Date: Sat, 22 Aug 2015 06:13:04 +0200 Subject: [PATCH 3/5] arrays shouldn't be treated as an iterable --- src/react/JSONTree/obj-type.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/react/JSONTree/obj-type.js b/src/react/JSONTree/obj-type.js index 40dfd8d1a0..fb12060633 100644 --- a/src/react/JSONTree/obj-type.js +++ b/src/react/JSONTree/obj-type.js @@ -1,5 +1,7 @@ export default function(obj) { - if (obj !== null && typeof obj === 'object' && typeof obj[Symbol.iterator] === 'function') { + if (obj !== null && typeof obj === 'object' && !Array.isArray(obj) && + typeof obj[Symbol.iterator] === 'function' + ) { return 'Iterable'; } return Object.prototype.toString.call(obj).slice(8, -1); From 11af53ccfbc798439fe1e8b4d1490278d1b1ce2f Mon Sep 17 00:00:00 2001 From: FredyC Date: Sat, 22 Aug 2015 17:38:43 +0200 Subject: [PATCH 4/5] counting entries of iterable structure --- src/react/JSONTree/JSONIterableNode.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/react/JSONTree/JSONIterableNode.js b/src/react/JSONTree/JSONIterableNode.js index 3181adb252..d4a5800b4f 100644 --- a/src/react/JSONTree/JSONIterableNode.js +++ b/src/react/JSONTree/JSONIterableNode.js @@ -87,10 +87,12 @@ export default class JSONIterableNode extends React.Component { if (!this.itemString) { const { data } = this.props; let count = 0; - if (typeof data.count === 'function') { - count = data.count(); + if (typeof data.size !== 'undefined') { + count = data.size; } else { - count = data.length; + for (const entry of data) { // eslint-disable-line no-unused-vars + count += 1; + } } this.itemString = count + ' entr' + (count !== 1 ? 'ies' : 'y'); } From fff7e3987429ec3f93e2d07bfb084258c59dba28 Mon Sep 17 00:00:00 2001 From: FredyC Date: Sat, 22 Aug 2015 17:51:37 +0200 Subject: [PATCH 5/5] using Number.isSafeInteger for iterable size (+ fixed some comments) --- src/react/JSONTree/JSONIterableNode.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/react/JSONTree/JSONIterableNode.js b/src/react/JSONTree/JSONIterableNode.js index d4a5800b4f..280e991830 100644 --- a/src/react/JSONTree/JSONIterableNode.js +++ b/src/react/JSONTree/JSONIterableNode.js @@ -50,7 +50,7 @@ export default class JSONIterableNode extends React.Component { }; } - // Returns the child nodes for each element in the array. If we have + // Returns the child nodes for each entry in iterable. If we have // generated them previously, we return from cache, otherwise we create // them. getChildNodes() { @@ -81,13 +81,13 @@ export default class JSONIterableNode extends React.Component { return this.renderedChildren; } - // Returns the "n Items" string for this node, generating and + // Returns the "n entries" string for this node, generating and // caching it if it hasn't been created yet. getItemString() { if (!this.itemString) { const { data } = this.props; let count = 0; - if (typeof data.size !== 'undefined') { + if (Number.isSafeInteger(data.size)) { count = data.size; } else { for (const entry of data) { // eslint-disable-line no-unused-vars