import React from 'react'; import PropTypes from 'prop-types'; import JSONArrow from './JSONArrow'; import getCollectionEntries from './getCollectionEntries'; import JSONNode from './JSONNode'; import ItemRange from './ItemRange'; /** * Renders nested values (eg. objects, arrays, lists, etc.) */ function renderChildNodes(props, from, to) { const { nodeType, data, collectionLimit, circularCache, keyPath, postprocessValue, sortObjectKeys } = props; const childNodes = []; getCollectionEntries( nodeType, data, sortObjectKeys, collectionLimit, from, to ).forEach(entry => { if (entry.to) { childNodes.push( ); } else { const { key, value } = entry; const isCircular = circularCache.indexOf(value) !== -1; const node = ( ); if (node !== false) { childNodes.push(node); } } }); return childNodes; } function getStateFromProps(props) { // calculate individual node expansion if necessary const expanded = props.shouldExpandNode && !props.isCircular ? props.shouldExpandNode(props.keyPath, props.data, props.level) : false; return { expanded }; } export default class JSONNestedNode extends React.Component { static propTypes = { getItemString: PropTypes.func.isRequired, nodeTypeIndicator: PropTypes.any, nodeType: PropTypes.string.isRequired, data: PropTypes.any, hideRoot: PropTypes.bool.isRequired, createItemString: PropTypes.func.isRequired, styling: PropTypes.func.isRequired, collectionLimit: PropTypes.number, keyPath: PropTypes.arrayOf( PropTypes.oneOfType([PropTypes.string, PropTypes.number]) ).isRequired, labelRenderer: PropTypes.func.isRequired, shouldExpandNode: PropTypes.func, level: PropTypes.number.isRequired, sortObjectKeys: PropTypes.oneOfType([PropTypes.func, PropTypes.bool]), isCircular: PropTypes.bool, expandable: PropTypes.bool }; static defaultProps = { data: [], circularCache: [], level: 0, expandable: true }; constructor(props) { super(props); this.state = getStateFromProps(props); } componentWillReceiveProps(nextProps) { const nextState = getStateFromProps(nextProps); if (getStateFromProps(this.props).expanded !== nextState.expanded) { this.setState(nextState); } } shouldComponentUpdate(nextProps, nextState) { return ( !!Object.keys(nextProps).find( key => key !== 'circularCache' && (key === 'keyPath' ? nextProps[key].join('/') !== this.props[key].join('/') : nextProps[key] !== this.props[key]) ) || nextState.expanded !== this.state.expanded ); } render() { const { getItemString, nodeTypeIndicator, nodeType, data, hideRoot, createItemString, styling, collectionLimit, keyPath, labelRenderer, expandable } = this.props; const { expanded } = this.state; const renderedChildren = expanded || (hideRoot && this.props.level === 0) ? renderChildNodes({ ...this.props, level: this.props.level + 1 }) : null; const itemType = ( {nodeTypeIndicator} ); const renderedItemString = getItemString( nodeType, data, itemType, createItemString(data, collectionLimit) ); const stylingArgs = [keyPath, nodeType, expanded, expandable]; return hideRoot ? (
    • {renderedChildren}
  • ) : (
  • {expandable && ( )} {renderedItemString}
      {renderedChildren}
  • ); } handleClick = () => { if (this.props.expandable) { this.setState({ expanded: !this.state.expanded }); } }; }