import React from 'react' ; import PropTypes from 'prop-types' ; import JSONNode from './JSONNode' ; import createStylingFromTheme from './createStylingFromTheme' ; import { invertTheme , StylingConfig , StylingFunction , StylingValue , Theme , } from 'react-base16-styling' ; import { CircularPropsPassedThroughJSONTree } from './types' ; interface Props extends CircularPropsPassedThroughJSONTree { data : any ; theme ? : Theme ; invertTheme : boolean ; } interface State { styling : StylingFunction ; } const identity = ( value ) => value ; const expandRootNode = ( keyPath , data , level ) => level === 0 ; const defaultItemString = ( type , data , itemType , itemString ) => ( < span > { itemType } { itemString } < /span>); const defaultLabelRenderer = ( [ label ] ) => ( < span > { label } : < /span>); const noCustomNode = ( ) => false ; 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 as keyof Props ] ) ; if ( deprecatedStylingMethods . length > 0 ) { if ( typeof theme === 'string' ) { theme = { extend : theme , } ; } else { theme = { ... theme } ; } deprecatedStylingMethods . forEach ( ( name ) => { console . error ( `Styling method "${ name }" is deprecated, use "theme" property instead` ) ; ( theme as StylingConfig ) [ deprecatedStylingMethodsMap [ name as keyof typeof deprecatedStylingMethodsMap ] ] = ( { style } , ... args ) => ( { style : { ... style , ... props [ name as keyof Props ] ( ... args ) , } , } ) ; } ) ; } return theme ; } function getStateFromProps ( props ) { let theme = checkLegacyTheming ( props . theme , props ) ; if ( props . invertTheme ) { theme = invertTheme ( theme ) ; } return { styling : createStylingFromTheme ( theme ) , } ; } export class JSONTree extends React . Component < Props , State > { static propTypes = { data : PropTypes . any , hideRoot : PropTypes . bool , theme : PropTypes . oneOfType ( [ PropTypes . object , PropTypes . string ] ) , invertTheme : PropTypes . bool , keyPath : PropTypes . arrayOf ( PropTypes . oneOfType ( [ PropTypes . string , PropTypes . number ] ) ) , postprocessValue : PropTypes . func , sortObjectKeys : PropTypes . oneOfType ( [ PropTypes . func , PropTypes . bool ] ) , } ; static defaultProps = { shouldExpandNode : expandRootNode , hideRoot : false , keyPath : [ 'root' ] , getItemString : defaultItemString , labelRenderer : defaultLabelRenderer , valueRenderer : identity , postprocessValue : identity , isCustomNode : noCustomNode , collectionLimit : 50 , invertTheme : true , } ; constructor ( props ) { super ( props ) ; this . state = getStateFromProps ( props ) ; } UNSAFE_componentWillReceiveProps ( nextProps ) { if ( [ 'theme' , 'invertTheme' ] . find ( ( k ) => nextProps [ k as keyof Props ] !== this . props [ k as keyof Props ] ) ) { this . setState ( getStateFromProps ( nextProps ) ) ; } } shouldComponentUpdate ( nextProps ) { return ! ! Object . keys ( nextProps ) . find ( ( k ) => k === 'keyPath' ? nextProps [ k ] . join ( '/' ) !== this . props [ k ] . join ( '/' ) : nextProps [ k as keyof Props ] !== this . props [ k as keyof Props ] ) ; } render ( ) { const { data : value , keyPath , postprocessValue , hideRoot , theme , invertTheme : _ , ... rest } = this . props ; const { styling } = this . state ; return ( < ul { ... styling ( 'tree' ) } > < JSONNode { ... { postprocessValue , hideRoot , styling , ... rest } } keyPath = { hideRoot ? [ ] : keyPath } value = { postprocessValue ( value ) } / > < /ul>); } } export { StylingValue } ;