import HolderOutlined from '@ant-design/icons/HolderOutlined'; import classNames from 'classnames'; import type { BasicDataNode, TreeProps as RcTreeProps } from 'rc-tree'; import RcTree from 'rc-tree'; import type { DataNode, Key } from 'rc-tree/lib/interface'; import type { Component } from 'react'; import React from 'react'; import { ConfigContext } from '../config-provider'; import initCollapseMotion from '../_util/motion'; import dropIndicatorRender from './utils/dropIndicator'; import renderSwitcherIcon from './utils/iconUtil'; import useStyle from './style'; export type SwitcherIcon = React.ReactNode | ((props: AntTreeNodeProps) => React.ReactNode); export type TreeLeafIcon = React.ReactNode | ((props: AntTreeNodeProps) => React.ReactNode); export interface AntdTreeNodeAttribute { eventKey: string; prefixCls: string; className: string; expanded: boolean; selected: boolean; checked: boolean; halfChecked: boolean; children: React.ReactNode; title: React.ReactNode; pos: string; dragOver: boolean; dragOverGapTop: boolean; dragOverGapBottom: boolean; isLeaf: boolean; selectable: boolean; disabled: boolean; disableCheckbox: boolean; } export interface AntTreeNodeProps { className?: string; checkable?: boolean; disabled?: boolean; disableCheckbox?: boolean; title?: string | React.ReactNode; key?: Key; eventKey?: string; isLeaf?: boolean; checked?: boolean; expanded?: boolean; loading?: boolean; selected?: boolean; selectable?: boolean; icon?: ((treeNode: AntdTreeNodeAttribute) => React.ReactNode) | React.ReactNode; children?: React.ReactNode; [customProp: string]: any; } export interface AntTreeNode extends Component {} export interface AntTreeNodeBaseEvent { node: AntTreeNode; nativeEvent: MouseEvent; } export interface AntTreeNodeCheckedEvent extends AntTreeNodeBaseEvent { event: 'check'; checked?: boolean; checkedNodes?: AntTreeNode[]; } export interface AntTreeNodeSelectedEvent extends AntTreeNodeBaseEvent { event: 'select'; selected?: boolean; selectedNodes?: DataNode[]; } export interface AntTreeNodeExpandedEvent extends AntTreeNodeBaseEvent { expanded?: boolean; } export interface AntTreeNodeMouseEvent { node: AntTreeNode; event: React.DragEvent; } export interface AntTreeNodeDragEnterEvent extends AntTreeNodeMouseEvent { expandedKeys: Key[]; } export interface AntTreeNodeDropEvent { node: AntTreeNode; dragNode: AntTreeNode; dragNodesKeys: Key[]; dropPosition: number; dropToGap?: boolean; event: React.MouseEvent; } // [Legacy] Compatible for v3 export type TreeNodeNormal = DataNode; type DraggableFn = (node: DataNode) => boolean; interface DraggableConfig { icon?: React.ReactNode | false; nodeDraggable?: DraggableFn; } export interface TreeProps extends Omit< RcTreeProps, 'prefixCls' | 'showLine' | 'direction' | 'draggable' | 'icon' | 'switcherIcon' > { showLine?: boolean | { showLeafIcon: boolean | TreeLeafIcon }; className?: string; /** 是否支持多选 */ multiple?: boolean; /** 是否自动展开父节点 */ autoExpandParent?: boolean; /** Checkable状态下节点选择完全受控(父子节点选中状态不再关联) */ checkStrictly?: boolean; /** 是否支持选中 */ checkable?: boolean; /** 是否禁用树 */ disabled?: boolean; /** 默认展开所有树节点 */ defaultExpandAll?: boolean; /** 默认展开对应树节点 */ defaultExpandParent?: boolean; /** 默认展开指定的树节点 */ defaultExpandedKeys?: Key[]; /** (受控)展开指定的树节点 */ expandedKeys?: Key[]; /** (受控)选中复选框的树节点 */ checkedKeys?: Key[] | { checked: Key[]; halfChecked: Key[] }; /** 默认选中复选框的树节点 */ defaultCheckedKeys?: Key[]; /** (受控)设置选中的树节点 */ selectedKeys?: Key[]; /** 默认选中的树节点 */ defaultSelectedKeys?: Key[]; selectable?: boolean; /** 点击树节点触发 */ filterAntTreeNode?: (node: AntTreeNode) => boolean; loadedKeys?: Key[]; /** 设置节点可拖拽(IE>8) */ draggable?: DraggableFn | boolean | DraggableConfig; style?: React.CSSProperties; showIcon?: boolean; icon?: | ((nodeProps: AntdTreeNodeAttribute) => React.ReactNode) | React.ReactNode | RcTreeProps['icon']; switcherIcon?: SwitcherIcon | RcTreeProps['switcherIcon']; prefixCls?: string; children?: React.ReactNode; blockNode?: boolean; } const Tree = React.forwardRef((props, ref) => { const { getPrefixCls, direction, virtual } = React.useContext(ConfigContext); const { prefixCls: customizePrefixCls, className, showIcon = false, showLine, switcherIcon, blockNode = false, children, checkable = false, selectable = true, draggable, motion: customMotion, } = props; const prefixCls = getPrefixCls('tree', customizePrefixCls); const rootPrefixCls = getPrefixCls(); const motion = customMotion ?? { ...initCollapseMotion(rootPrefixCls), motionAppear: false, }; const newProps = { ...props, checkable, selectable, showIcon, motion, blockNode, showLine: Boolean(showLine), dropIndicatorRender, }; const [wrapSSR, hashId] = useStyle(prefixCls); const draggableConfig = React.useMemo(() => { if (!draggable) { return false; } let mergedDraggable: DraggableConfig = {}; switch (typeof draggable) { case 'function': mergedDraggable.nodeDraggable = draggable; break; case 'object': mergedDraggable = { ...draggable }; break; default: break; // Do nothing } if (mergedDraggable.icon !== false) { mergedDraggable.icon = mergedDraggable.icon || ; } return mergedDraggable; }, [draggable]); return wrapSSR( : checkable} selectable={selectable} switcherIcon={(nodeProps: AntTreeNodeProps) => renderSwitcherIcon(prefixCls, switcherIcon, showLine, nodeProps) } draggable={draggableConfig} > {children} , ); }); if (process.env.NODE_ENV !== 'production') { Tree.displayName = 'Tree'; } export default Tree;