import { View } from "react-native";
import React, {FC, PropsWithChildren, Dispatch, SetStateAction, useState } from "react";
// import { FCC } from "app/types/IReact"; // FCC = Functional Component with children prop
type FCC<P = {}> = FC<PropsWithChildren<P>>;
type WithId<T> = T & { $id: string };
type NodeWithNodesProp<
NKey extends string,
LKey extends string,
N extends WithId<NodeWithNodesAndLeafsProp<NKey, LKey, N, L>>,
L extends WithId<{}>,
> = {
[Nkey in NKey]: WithId<NodeWithNodesAndLeafsProp<NKey, LKey, N, L>>[];
};
type NodeWithLeafsProp<LKey extends string> = {
[Lkey in LKey]: WithId<{}>[];
};
type NodeWithNodesAndLeafsProp<
NKey extends string,
LKey extends string,
N extends WithId<NodeWithNodesAndLeafsProp<NKey, LKey, N, L>>,
L extends WithId<{}>,
> = WithId<NodeWithNodesProp<NKey, LKey, N, L> & NodeWithLeafsProp<LKey>>;
interface ITreeView<
NKey extends string,
LKey extends string,
N extends WithId<NodeWithNodesAndLeafsProp<NKey, LKey, N, L>>,
L extends WithId<{}>,
> {
treeData: {
nodes: N[];
rootLeafs: L[];
};
isCollapsed: boolean;
nodeArrKey: NKey;
leafArrKey: LKey;
renderNode: (
node: N,
setCollapse: Dispatch<SetStateAction<boolean>>,
) => JSX.Element;
renderLeaf: (node: L) => JSX.Element;
}
#Argument of type 'N[NKey]' is not assignable to parameter of type 'N[]'.
3 messages · Page 1 of 1 (latest)
export const TreeView = <
NKey extends string,
LKey extends string,
N extends WithId<NodeWithNodesAndLeafsProp<NKey, LKey, N, L>>,
L extends WithId<{}>,
>(
props: ITreeView<NKey, LKey, N, L>,
) => {
const { treeData, nodeArrKey, leafArrKey, renderNode, renderLeaf } = props;
const _renderNodes = (nodesArr: (typeof treeData)["nodes"]) => {
return nodesArr.map(node => {
const nodeObj = node[nodeArrKey];
const childNodes = nodeObj && _renderNodes(nodeObj); // here's the TS Error
const leafObj = node[leafArrKey];
const childLeafs = leafObj && _renderLeaf(leafObj);
return (
<TreeWrapper
isCollapsed={props.isCollapsed}
key={node.$id}
node={setCollapse => renderNode && renderNode(node, setCollapse)}
childNodes={childNodes}
childLeafs={childLeafs}
/>
);
});
};
const _renderLeaf = (leaf: (typeof treeData)["rootLeafs"]) => {
return leaf.map(node => {
return (
<React.Fragment key={node.$id}>
{renderLeaf && renderLeaf(node)}
</React.Fragment>
);
});
};
return (
<View>
{_renderNodes(treeData["nodes"])}
{_renderLeaf(treeData["rootLeafs"])}
</View>
);
};
.
EXTRAS
interface ITreeWrapper {
isCollapsed: boolean;
node: (setCollapse: Dispatch<SetStateAction<boolean>>) => JSX.Element;
childNodes: JSX.Element[];
childLeafs: JSX.Element[];
}
const TreeWrapper: FCC<ITreeWrapper> = props => {
const { node, childNodes, childLeafs, isCollapsed } = props;
const [wrapperMinHt, setWrapperMinHt] = useState(0);
const [collapse, setCollapse] = useState(isCollapsed);
const allowCollapse = collapse && !!wrapperMinHt;
const wrapperHt = allowCollapse ? wrapperMinHt : "auto";
// TODO: animate height change
return (
<View style={{ height: wrapperHt, overflow: "hidden" }}>
<View onLayout={e => setWrapperMinHt(e.nativeEvent.layout.height)}>
{node(setCollapse)}
</View>
{childNodes}
{childLeafs}
</View>
);
};
/////////////////////////////////// Usage of the TreeView component//////////////////////////
type IBookmark = {
$id: string;
// ..... others
}
type IFolder = {
$id: string;
folders: IFolder[];
bookmarks: IBookmark[];
/// ..... others
}
export function TreePanel() {
return (
<TreeView
treeData={{
nodes: [] as IFolder[],
rootLeafs: [] as IBookmark[],
}}
isCollapsed
nodeArrKey="folders"
leafArrKey="bookmarks"
renderNode={(node, setCollapse) => (<React.Fragment></React.Fragment>)}
renderLeaf={node => <React.Fragment></React.Fragment>}
/>
);
}