shouldExpandNode => shouldExpandNodeInitially

This commit is contained in:
Nathan Bierema 2023-01-03 09:05:13 -05:00
parent 9c4307780f
commit 5175bc0a00
4 changed files with 83 additions and 123 deletions

View File

@ -35,6 +35,7 @@ const JSONIterableNode: React.FunctionComponent<Props> = ({ ...props }) => {
nodeType="Iterable" nodeType="Iterable"
nodeTypeIndicator="()" nodeTypeIndicator="()"
createItemString={createItemString} createItemString={createItemString}
expandable
/> />
); );
}; };

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { useCallback, useState } from 'react';
import JSONArrow from './JSONArrow'; import JSONArrow from './JSONArrow';
import getCollectionEntries from './getCollectionEntries'; import getCollectionEntries from './getCollectionEntries';
import JSONNode from './JSONNode'; import JSONNode from './JSONNode';
@ -91,128 +91,86 @@ interface Props extends CommonInternalProps {
nodeTypeIndicator: string; nodeTypeIndicator: string;
createItemString: (data: any, collectionLimit: number) => string; createItemString: (data: any, collectionLimit: number) => string;
expandable: boolean; expandable: boolean;
circularCache: CircularCache;
level: number;
} }
interface State { export default function JSONNestedNode(props: Props) {
expanded: boolean; const {
} circularCache = [],
collectionLimit,
createItemString,
data,
expandable,
getItemString,
hideRoot,
isCircular,
keyPath,
labelRenderer,
level = 0,
nodeType,
nodeTypeIndicator,
shouldExpandNodeInitially,
styling,
} = props;
function getStateFromProps(props: Props) { const [expanded, setExpanded] = useState<boolean>(
// calculate individual node expansion if necessary // calculate individual node expansion if necessary
const expanded = !props.isCircular isCircular ? false : shouldExpandNodeInitially(keyPath, data, level)
? props.shouldExpandNode(props.keyPath, props.data, props.level) );
: false;
return {
expanded,
};
}
export default class JSONNestedNode extends React.Component<Props, State> { const handleClick = useCallback(() => {
static defaultProps = { if (expandable) setExpanded(!expanded);
data: [], }, [expandable, expanded]);
circularCache: [],
level: 0,
expandable: true,
};
constructor(props: Props) { const renderedChildren =
super(props); expanded || (hideRoot && level === 0)
this.state = getStateFromProps(props); ? renderChildNodes({ ...props, circularCache, level: level + 1 })
} : null;
UNSAFE_componentWillReceiveProps(nextProps: Props) { const itemType = (
const nextState = getStateFromProps(nextProps); <span {...styling('nestedNodeItemType', expanded)}>
if (getStateFromProps(this.props).expanded !== nextState.expanded) { {nodeTypeIndicator}
this.setState(nextState); </span>
} );
} const renderedItemString = getItemString(
nodeType,
data,
itemType,
createItemString(data, collectionLimit),
keyPath
);
const stylingArgs = [keyPath, nodeType, expanded, expandable] as const;
shouldComponentUpdate(nextProps: Props, nextState: State) { return hideRoot ? (
return ( <li {...styling('rootNode', ...stylingArgs)}>
!!Object.keys(nextProps).find( <ul {...styling('rootNodeChildren', ...stylingArgs)}>
(key) => {renderedChildren}
key !== 'circularCache' && </ul>
(key === 'keyPath' </li>
? nextProps[key].join('/') !== this.props[key].join('/') ) : (
: nextProps[key as keyof Props] !== this.props[key as keyof Props]) <li {...styling('nestedNode', ...stylingArgs)}>
) || nextState.expanded !== this.state.expanded {expandable && (
); <JSONArrow
} styling={styling}
nodeType={nodeType}
render() { expanded={expanded}
const { onClick={handleClick}
getItemString, />
nodeTypeIndicator, )}
nodeType, <label
data, {...styling(['label', 'nestedNodeLabel'], ...stylingArgs)}
hideRoot, onClick={handleClick}
createItemString, >
styling, {labelRenderer(...stylingArgs)}
collectionLimit, </label>
keyPath, <span
labelRenderer, {...styling('nestedNodeItemString', ...stylingArgs)}
expandable, onClick={handleClick}
} = this.props; >
const { expanded } = this.state; {renderedItemString}
const renderedChildren =
expanded || (hideRoot && this.props.level === 0)
? renderChildNodes({ ...this.props, level: this.props.level + 1 })
: null;
const itemType = (
<span {...styling('nestedNodeItemType', expanded)}>
{nodeTypeIndicator}
</span> </span>
); <ul {...styling('nestedNodeChildren', ...stylingArgs)}>
const renderedItemString = getItemString( {renderedChildren}
nodeType, </ul>
data, </li>
itemType, );
createItemString(data, collectionLimit),
keyPath
);
const stylingArgs = [keyPath, nodeType, expanded, expandable] as const;
return hideRoot ? (
<li {...styling('rootNode', ...stylingArgs)}>
<ul {...styling('rootNodeChildren', ...stylingArgs)}>
{renderedChildren}
</ul>
</li>
) : (
<li {...styling('nestedNode', ...stylingArgs)}>
{expandable && (
<JSONArrow
styling={styling}
nodeType={nodeType}
expanded={expanded}
onClick={this.handleClick}
/>
)}
<label
{...styling(['label', 'nestedNodeLabel'], ...stylingArgs)}
onClick={this.handleClick}
>
{labelRenderer(...stylingArgs)}
</label>
<span
{...styling('nestedNodeItemString', ...stylingArgs)}
onClick={this.handleClick}
>
{renderedItemString}
</span>
<ul {...styling('nestedNodeChildren', ...stylingArgs)}>
{renderedChildren}
</ul>
</li>
);
}
handleClick = () => {
if (this.props.expandable) {
this.setState({ expanded: !this.state.expanded });
}
};
} }

View File

@ -13,7 +13,7 @@ import type {
GetItemString, GetItemString,
IsCustomNode, IsCustomNode,
LabelRenderer, LabelRenderer,
ShouldExpandNode, ShouldExpandNodeInitially,
} from './types'; } from './types';
interface Props extends Partial<CommonExternalProps> { interface Props extends Partial<CommonExternalProps> {
@ -23,7 +23,8 @@ interface Props extends Partial<CommonExternalProps> {
} }
const identity = (value: any) => value; const identity = (value: any) => value;
const expandRootNode: ShouldExpandNode = (keyPath, data, level) => level === 0; const expandRootNode: ShouldExpandNodeInitially = (keyPath, data, level) =>
level === 0;
const defaultItemString: GetItemString = (type, data, itemType, itemString) => ( const defaultItemString: GetItemString = (type, data, itemType, itemString) => (
<span> <span>
{itemType} {itemString} {itemType} {itemString}
@ -39,7 +40,7 @@ export function JSONTree({
keyPath = ['root'], keyPath = ['root'],
labelRenderer = defaultLabelRenderer, labelRenderer = defaultLabelRenderer,
valueRenderer = identity, valueRenderer = identity,
shouldExpandNode = expandRootNode, shouldExpandNodeInitially = expandRootNode,
hideRoot = false, hideRoot = false,
getItemString = defaultItemString, getItemString = defaultItemString,
postprocessValue = identity, postprocessValue = identity,
@ -62,7 +63,7 @@ export function JSONTree({
styling={styling} styling={styling}
labelRenderer={labelRenderer} labelRenderer={labelRenderer}
valueRenderer={valueRenderer} valueRenderer={valueRenderer}
shouldExpandNode={shouldExpandNode} shouldExpandNodeInitially={shouldExpandNodeInitially}
hideRoot={hideRoot} hideRoot={hideRoot}
getItemString={getItemString} getItemString={getItemString}
postprocessValue={postprocessValue} postprocessValue={postprocessValue}

View File

@ -26,7 +26,7 @@ export type ValueRenderer = (
...keyPath: KeyPath ...keyPath: KeyPath
) => React.ReactNode; ) => React.ReactNode;
export type ShouldExpandNode = ( export type ShouldExpandNodeInitially = (
keyPath: KeyPath, keyPath: KeyPath,
data: any, data: any,
level: number level: number
@ -46,7 +46,7 @@ export interface CommonExternalProps {
keyPath: KeyPath; keyPath: KeyPath;
labelRenderer: LabelRenderer; labelRenderer: LabelRenderer;
valueRenderer: ValueRenderer; valueRenderer: ValueRenderer;
shouldExpandNode: ShouldExpandNode; shouldExpandNodeInitially: ShouldExpandNodeInitially;
hideRoot: boolean; hideRoot: boolean;
getItemString: GetItemString; getItemString: GetItemString;
postprocessValue: PostprocessValue; postprocessValue: PostprocessValue;