import React from "react";
import PropTypes from "prop-types";
import TreeNode from "./TreeNode";
import { DEFAULT_ROOT_PATH, hasChildNodes, getExpandedPaths } from "./pathUtils";
const reducer = (state, action) => {
if (action.type === "TOGGLE_EXPAND") {
let { path } = action;
if (!Array.isArray(path)) {
path = [path];
}
const ret = { ...state };
path.forEach((objPath) => {
const { expandedPaths } = state;
const expanded = !!expandedPaths[objPath];
ret.expandedPaths[objPath] = !expanded;
});
return ret;
} else {
return state;
}
};
class ConnectedTreeNode extends React.Component<{
name: string;
data: any;
dataIterator: Function;
depth: number;
expanded: boolean;
nodeRenderer: (args: any) => JSX.Element;
filterStrings: string[];
searchBoxText: string;
path: string;
onMatchFound: Function;
}> {
state: {
depth: number;
expandedPaths: string[];
};
constructor(props, context) {
super(props);
this.state = context.store.storeState;
this.state.depth = props.depth;
}
shouldComponentUpdate(nextProps, nextState) {
const filterChanged = this.props.filterStrings.every((str) => nextProps.filterStrings.includes(str));
const searchBoxTextChanged = nextProps.searchBoxText !== this.props.searchBoxText;
return (
filterChanged ||
searchBoxTextChanged ||
!!nextState.expandedPaths[nextProps.path] !== !!this.state.expandedPaths[this.props.path] ||
nextProps.data !== this.props.data ||
nextProps.name !== this.props.name
);
}
handleRightClick(path: string) {
this.handleClick(path);
}
handleClick(path: string) {
this.context.store.storeState = reducer(this.context.store.storeState, {
type: "TOGGLE_EXPAND",
path,
});
this.setState(this.context.store.storeState);
}
renderChildNodes(parentData, parentPath) {
const { dataIterator } = this.props;
const { depth } = this.props;
const { onMatchFound, nodeRenderer, searchBoxText, filterStrings, expanded } = this.props;
const childNodes: any[] = [];
for (const { name, data } of dataIterator(parentData)) {
const key = name;
const path = `${parentPath}.${key}`;
childNodes.push(
);
}
return childNodes;
}
render() {
const { data, dataIterator, path, depth, name } = this.props;
const nodeHasChildNodes = hasChildNodes(data, dataIterator);
const { expandedPaths } = this.state;
const expanded = !!expandedPaths[path];
const { nodeRenderer } = this.props;
return (
{
this.handleRightClick(path);
}}
onClick={
nodeHasChildNodes
? () => {
this.handleClick(path);
}
: () => {}
}
// show arrow anyway even if not expanded and not rendering children
shouldShowArrow={nodeHasChildNodes}
// show placeholder only for non root nodes
shouldShowPlaceholder={depth > 0}
// Render a node from name and data (or possibly other props like isNonenumerable)
nodeRenderer={nodeRenderer}
name={name}
data={data}
>
{
// only render if the node is expanded
expanded ? this.renderChildNodes(data, path) : undefined
}
);
}
}
// @ts-ignore Convert to modern React context
ConnectedTreeNode.contextTypes = {
store: PropTypes.any,
};
/**
* See ObjectInspector for description of props
*/
class TreeView extends React.PureComponent<{
name: string;
data: any;
dataIterator: Function;
nodeRenderer: (args: any) => JSX.Element;
filterStrings: string[];
searchBoxText: string;
onMatchFound: Function;
expanded: boolean;
expandPaths?: string[];
expandLevel?: number;
}> {
store: any;
static defaultProps: {
expandLevel: number;
expandPaths: string[];
} = {
expandLevel: 0,
expandPaths: [],
};
static childContextTypes = {
store: PropTypes.any,
};
constructor(props) {
super(props);
this.state = {
expandLevel: 0,
};
this.store = {
storeState: {
expandedPaths: getExpandedPaths(props.data, props.dataIterator, props.expandPaths, props.expandLevel),
},
};
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.store = {
storeState: {
expandedPaths: getExpandedPaths(
nextProps.data,
nextProps.dataIterator,
nextProps.expandPaths,
nextProps.expandLevel,
this.store.storeState.expandedPaths
),
},
};
}
getChildContext() {
return {
store: this.store,
};
}
render() {
const { name, data, dataIterator, expanded, nodeRenderer, filterStrings, searchBoxText, onMatchFound } = this.props;
const rootPath = DEFAULT_ROOT_PATH;
return (
);
}
}
export default TreeView;