react-native-json-tree
Version:
React Native JSON viewing component, based on react-json-tree
149 lines (129 loc) • 3.82 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import { Text, View } from 'react-native';
import { JSONNode } from './Nodes';
import createStylingFromTheme from './createStylingFromTheme';
import { invertTheme } from 'react-base16-styling';
const identity = value => value;
const expandRootNode = (_keyName, _data, level) => level === 0;
const defaultItemString = (_type, _data, itemType, itemString) => (
<Text>
{itemType} {itemString}
</Text>
);
const defaultLabelRenderer = ([label]) => <Text>{label}:</Text>;
const noCustomNode = () => false;
/* eslint-disable no-param-reassign */
function checkLegacyTheming(theme, props) {
const deprecatedStylingMethodsMap = {
getArrowStyle: 'arrow',
getListStyle: 'nestedNodeChildren',
getItemStringStyle: 'nestedNodeItemString',
getLabelStyle: 'label',
getValueStyle: 'valueText',
};
const deprecatedStylingMethods = Object.keys(
deprecatedStylingMethodsMap
).filter(name => props[name]);
if (deprecatedStylingMethods.length > 0) {
if (typeof theme === 'string') {
theme = { extend: theme };
} else {
theme = { ...theme };
}
deprecatedStylingMethods.forEach(name => {
// eslint-disable-next-line no-console
console.error(
`Styling method "${name}" is deprecated, use the "theme" property instead`
);
theme[deprecatedStylingMethodsMap[name]] = ({ style }, ...args) => ({
style: {
...style,
...props[name](...args),
},
});
});
}
return theme;
}
function getStateFromProps(props) {
let theme = checkLegacyTheming(props.theme, props);
if (props.invertTheme) {
theme = invertTheme(theme);
}
return {
styling: createStylingFromTheme(theme),
};
}
/* eslint-enable no-param-reassign */
class JSONTree extends React.Component {
static propTypes = {
data: PropTypes.oneOfType([
PropTypes.array,
PropTypes.bool,
PropTypes.number,
PropTypes.object,
PropTypes.string,
]).isRequired,
hideRoot: PropTypes.bool,
invertTheme: PropTypes.bool,
keyPath: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.string, PropTypes.number])
),
postprocessValue: PropTypes.func,
sortObjectKeys: PropTypes.oneOfType([PropTypes.func, PropTypes.bool]),
theme: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
};
static defaultProps = {
shouldExpandNode: expandRootNode,
hideRoot: false,
keyPath: ['root'],
getItemString: defaultItemString,
labelRenderer: defaultLabelRenderer,
valueRenderer: identity,
postprocessValue: identity,
isCustomNode: noCustomNode,
collectionLimit: 50,
invertTheme: true,
sortObjectKeys: true,
};
constructor(props) {
super(props);
this.state = getStateFromProps(props);
}
static getDerivedStateFromProps(props, state) {
if (['theme', 'invertTheme'].find(k => props[k] !== state[k])) {
return getStateFromProps(props);
}
return null;
}
shouldComponentUpdate(nextProps) {
return !!Object.keys(nextProps).find(k =>
k === 'keyPath'
? nextProps[k].join('/') !== this.props[k].join('/')
: nextProps[k] !== this.props[k]
);
}
render() {
const {
data: value,
keyPath,
postprocessValue,
hideRoot,
theme, // eslint-disable-line no-unused-vars
invertTheme: _, // eslint-disable-line no-unused-vars
...rest
} = this.props;
const { styling } = this.state;
return (
<View {...styling('tree')}>
<JSONNode
{...{ postprocessValue, hideRoot, styling, ...rest }}
keyPath={hideRoot ? [] : keyPath}
value={postprocessValue(value)}
/>
</View>
);
}
}
export default JSONTree;