import type { ReactNode, ComponentProps } from 'react';
import React, { useEffect, useState, createRef } from 'react';
import type { TreeProps } from 'rc-tree';
import Tree from 'rc-tree';
import type { EventDataNode } from 'rc-tree/lib/interface';
import type { CSSMotionProps, MotionEventHandler, MotionEndEventHandler } from 'rc-motion';
import './Tree.less';
const FileOutlined = ;
const FolderOpenOutlined = ;
const FolderOutlined = ;
const MinusSquareOutlined = ;
const PlusSquareOutlined = ;
function getTreeFromList(nodes: ReactNode, prefix = '') {
const data: TreeProps['treeData'] = [];
[].concat(nodes).forEach((node, i) => {
const key = `${prefix ? `${prefix}-` : ''}${i}`;
switch (node.type) {
case 'ul':
const parent = data[data.length - 1]?.children || data;
const ulLeafs = getTreeFromList(node.props.children || [], key);
parent.push(...ulLeafs);
break;
case 'li':
const liLeafs = getTreeFromList(node.props.children, key);
data.push({
title: [].concat(node.props.children).filter(child => child.type !== 'ul'),
key,
children: liLeafs,
isLeaf: !liLeafs.length,
});
break;
default:
}
});
return data;
}
const useListToTree = (nodes: ReactNode) => {
const [tree, setTree] = useState(getTreeFromList(nodes));
useEffect(() => {
setTree(getTreeFromList(nodes));
}, [nodes]);
return tree;
};
const getIcon = (props) => {
const { isLeaf, expanded } = props;
if (isLeaf) {
return FileOutlined;
}
return expanded ? FolderOpenOutlined : FolderOutlined;
}
const renderSwitcherIcon = (props) => {
const { isLeaf, expanded } = props;
if (isLeaf) {
return ;
}
return expanded ? (
{MinusSquareOutlined}
) : (
{PlusSquareOutlined}
);
}
// ================== Collapse Motion ==================
const getCollapsedHeight: MotionEventHandler = () => ({ height: 0, opacity: 0 });
const getRealHeight: MotionEventHandler = node => ({ height: node.scrollHeight, opacity: 1 });
const getCurrentHeight: MotionEventHandler = node => ({ height: node.offsetHeight });
const skipOpacityTransition: MotionEndEventHandler = (_, event) =>
(event as TransitionEvent).propertyName === 'height';
const collapseMotion: CSSMotionProps = {
motionName: 'ant-motion-collapse',
onAppearStart: getCollapsedHeight,
onEnterStart: getCollapsedHeight,
onAppearActive: getRealHeight,
onEnterActive: getRealHeight,
onLeaveStart: getCurrentHeight,
onLeaveActive: getCollapsedHeight,
onAppearEnd: skipOpacityTransition,
onEnterEnd: skipOpacityTransition,
onLeaveEnd: skipOpacityTransition,
motionDeadline: 500,
};
export default (props: ComponentProps<'div'>) => {
const data = useListToTree(props.children);
const treeRef = createRef();
const onClick = (event: React.MouseEvent, node: EventDataNode) =>{
const { isLeaf } = node;
if (isLeaf || event.shiftKey || event.metaKey || event.ctrlKey) {
return;
}
treeRef.current!.onNodeExpand(event as any, node);
};
return (
', children: data }]}
defaultExpandAll
switcherIcon={renderSwitcherIcon}
/>
);
};