| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 | 
							- import React, { PureComponent } from 'react';
 
- import cls from 'classnames';
 
- import PropTypes from 'prop-types';
 
- import { cssClasses } from '@douyinfe/semi-foundation/tree/constants';
 
- import { debounce, isFunction, isString } from 'lodash-es';
 
- import { IconTreeTriangleDown, IconFile, IconFolder, IconFolderOpen } from '@douyinfe/semi-icons';
 
- import { Checkbox } from '../checkbox';
 
- import TreeContext from './treeContext';
 
- import Spin from '../spin';
 
- import { TreeNodeProps, TreeNodeState } from './interface';
 
- const prefixcls = cssClasses.PREFIX_OPTION;
 
- export default class TreeNode extends PureComponent<TreeNodeProps, TreeNodeState> {
 
-     static contextType = TreeContext;
 
-     static propTypes = {
 
-         expanded: PropTypes.bool,
 
-         selected: PropTypes.bool,
 
-         checked: PropTypes.bool,
 
-         halfChecked: PropTypes.bool,
 
-         active: PropTypes.bool,
 
-         disabled: PropTypes.bool,
 
-         loaded: PropTypes.bool,
 
-         loading: PropTypes.bool,
 
-         isLeaf: PropTypes.bool,
 
-         pos: PropTypes.string,
 
-         children: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
 
-         icon: PropTypes.node,
 
-         directory: PropTypes.bool,
 
-         keyword: PropTypes.string,
 
-         treeNodeFilterProp: PropTypes.string,
 
-         selectedKey: PropTypes.string,
 
-         motionKey: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)])
 
-     };
 
-     static defaultProps = {
 
-         selectedKey: '',
 
-         motionKey: '',
 
-     };
 
-     debounceSelect: any;
 
-     refNode: HTMLElement;
 
-     constructor(props: TreeNodeProps) {
 
-         super(props);
 
-         this.state = {};
 
-         this.debounceSelect = debounce(this.onSelect, 500, {
 
-             leading: true,
 
-             trailing: false
 
-         });
 
-     }
 
-     onSelect = (e: React.MouseEvent) => {
 
-         const { onNodeSelect } = this.context;
 
-         onNodeSelect(e, this.props);
 
-     };
 
-     onExpand = (e: React.MouseEvent) => {
 
-         const { onNodeExpand } = this.context;
 
-         e && e.stopPropagation();
 
-         e.nativeEvent.stopImmediatePropagation();
 
-         onNodeExpand(e, this.props);
 
-     };
 
-     onCheck = (e: React.MouseEvent) => {
 
-         if (this.isDisabled()) {
 
-             return;
 
-         }
 
-         const { onNodeCheck } = this.context;
 
-         e.stopPropagation();
 
-         e.nativeEvent.stopImmediatePropagation();
 
-         onNodeCheck(e, this.props);
 
-     };
 
-     onContextMenu = (e: React.MouseEvent) => {
 
-         const { onNodeRightClick } = this.context;
 
-         e.stopPropagation();
 
-         e.nativeEvent.stopImmediatePropagation();
 
-         onNodeRightClick(e, this.props);
 
-     };
 
-     onClick = (e: React.MouseEvent) => {
 
-         const { expandAction } = this.context;
 
-         if (expandAction === 'doubleClick') {
 
-             this.debounceSelect(e);
 
-             return;
 
-         }
 
-         this.onSelect(e);
 
-         if (expandAction === 'click') {
 
-             this.onExpand(e);
 
-         }
 
-     };
 
-     onDoubleClick = (e: React.MouseEvent) => {
 
-         const { expandAction, onNodeDoubleClick } = this.context;
 
-         e.stopPropagation();
 
-         e.nativeEvent.stopImmediatePropagation();
 
-         if (isFunction(onNodeDoubleClick)) {
 
-             onNodeDoubleClick(e, this.props);
 
-         }
 
-         if (expandAction === 'doubleClick') {
 
-             this.onExpand(e);
 
-         }
 
-     };
 
-     onDragStart = (e: React.DragEvent) => {
 
-         const { onNodeDragStart } = this.context;
 
-         e.stopPropagation();
 
-         onNodeDragStart(e, { ...this.props, nodeInstance: this.refNode });
 
-         try {
 
-             // ie throw error
 
-             // firefox-need-it
 
-             e.dataTransfer.setData('text/plain', '');
 
-         } catch (error) {
 
-             // empty
 
-         }
 
-     };
 
-     onDragEnter = (e: React.DragEvent) => {
 
-         const { onNodeDragEnter } = this.context;
 
-         e.preventDefault();
 
-         e.stopPropagation();
 
-         onNodeDragEnter(e, { ...this.props, nodeInstance: this.refNode });
 
-     };
 
-     onDragOver = (e: React.DragEvent) => {
 
-         const { onNodeDragOver } = this.context;
 
-         e.preventDefault();
 
-         e.stopPropagation();
 
-         onNodeDragOver(e, { ...this.props, nodeInstance: this.refNode });
 
-     };
 
-     onDragLeave = (e: React.DragEvent) => {
 
-         const { onNodeDragLeave } = this.context;
 
-         e.stopPropagation();
 
-         onNodeDragLeave(e, { ...this.props, nodeInstance: this.refNode });
 
-     };
 
-     onDragEnd = (e: React.DragEvent) => {
 
-         const { onNodeDragEnd } = this.context;
 
-         e.stopPropagation();
 
-         onNodeDragEnd(e, { ...this.props, nodeInstance: this.refNode });
 
-     };
 
-     onDrop = (e: React.DragEvent) => {
 
-         const { onNodeDrop } = this.context;
 
-         e.preventDefault();
 
-         e.stopPropagation();
 
-         onNodeDrop(e, { ...this.props, nodeInstance: this.refNode });
 
-     };
 
-     getNodeChildren = () => {
 
-         const { children } = this.props;
 
-         return children || [];
 
-     };
 
-     isLeaf = () => {
 
-         const { isLeaf, loaded } = this.props;
 
-         const { loadData } = this.context;
 
-         const hasChildren = this.getNodeChildren().length !== 0;
 
-         if (isLeaf === false) {
 
-             return false;
 
-         }
 
-         return isLeaf || (!loadData && !hasChildren) || (loadData && loaded && !hasChildren);
 
-     };
 
-     isDisabled = () => {
 
-         const { disabled } = this.props;
 
-         const { treeDisabled } = this.context;
 
-         if (disabled === false) {
 
-             return false;
 
-         }
 
-         return Boolean(treeDisabled || disabled);
 
-     };
 
-     renderArrow() {
 
-         const showIcon = !this.isLeaf();
 
-         const { loading } = this.props;
 
-         if (loading) {
 
-             return <Spin wrapperClassName={`${prefixcls}-spin-icon`} />;
 
-         }
 
-         if (showIcon) {
 
-             return (
 
-                 <IconTreeTriangleDown
 
-                     className={`${prefixcls}-expand-icon`}
 
-                     size="small"
 
-                     onClick={this.onExpand}
 
-                 />
 
-             );
 
-         }
 
-         return (
 
-             <span className={`${prefixcls}-empty-icon`} />
 
-         );
 
-     }
 
-     renderCheckbox() {
 
-         const { checked, halfChecked } = this.props;
 
-         const disabled = this.isDisabled();
 
-         return (
 
-             <div onClick={this.onCheck}>
 
-                 <Checkbox
 
-                     indeterminate={halfChecked}
 
-                     checked={checked}
 
-                     disabled={Boolean(disabled)}
 
-                 />
 
-             </div>
 
-         );
 
-     }
 
-     renderIcon() {
 
-         const {
 
-             directory,
 
-             treeIcon
 
-         } = this.context;
 
-         const { expanded, icon } = this.props;
 
-         const hasChild = !this.isLeaf();
 
-         const hasIcon = icon || treeIcon;
 
-         let itemIcon;
 
-         if (hasIcon || directory) {
 
-             if (hasIcon) {
 
-                 itemIcon = icon || treeIcon;
 
-             } else {
 
-                 if (!hasChild) {
 
-                     itemIcon = <IconFile className={`${prefixcls}-item-icon`} />;
 
-                 } else {
 
-                     // eslint-disable-next-line max-len
 
-                     itemIcon = expanded ? <IconFolderOpen className={`${prefixcls}-item-icon`} /> : <IconFolder className={`${prefixcls}-item-icon`} />;
 
-                 }
 
-             }
 
-         }
 
-         return itemIcon;
 
-     }
 
-     renderEmptyNode() {
 
-         const { emptyContent } = this.props;
 
-         const wrapperCls = cls(prefixcls, {
 
-             [`${prefixcls}-empty`]: true,
 
-         });
 
-         return (
 
-             <ul className={wrapperCls}>
 
-                 <span className={`${prefixcls}-label ${prefixcls}-label-empty`}>
 
-                     {emptyContent}
 
-                 </span>
 
-             </ul>
 
-         );
 
-     }
 
-     renderRealLabel = () => {
 
-         const { renderLabel } = this.context;
 
-         const { label, keyword, data, filtered, treeNodeFilterProp } = this.props;
 
-         if (isFunction(renderLabel)) {
 
-             return renderLabel(label, data);
 
-         } else if (isString(label) && filtered && keyword && treeNodeFilterProp === 'label') {
 
-             const content: React.ReactNode[] = [];
 
-             label.split(keyword).forEach((node, index) => {
 
-                 if (index > 0) {
 
-                     content.push(
 
-                         <span className={`${prefixcls}-highlight`} key={index}>
 
-                             {keyword}
 
-                         </span>
 
-                     );
 
-                 }
 
-                 content.push(node);
 
-             });
 
-             return content;
 
-         } else {
 
-             return label;
 
-         }
 
-     };
 
-     setRef = (node: HTMLElement) => {
 
-         this.refNode = node;
 
-     };
 
-     // eslint-disable-next-line max-lines-per-function
 
-     render() {
 
-         const {
 
-             eventKey,
 
-             expanded,
 
-             selected,
 
-             checked,
 
-             halfChecked,
 
-             loading,
 
-             active,
 
-             level,
 
-             empty,
 
-             filtered,
 
-             treeNodeFilterProp,
 
-             // eslint-disable-next-line no-unused-vars
 
-             display,
 
-             style,
 
-             ...rest
 
-         } = this.props;
 
-         if (empty) {
 
-             return this.renderEmptyNode();
 
-         }
 
-         const {
 
-             multiple,
 
-             draggable,
 
-             renderFullLabel,
 
-             dragOverNodeKey,
 
-             dropPosition,
 
-             labelEllipsis
 
-         } = this.context;
 
-         const disabled = this.isDisabled();
 
-         const dragOver = dragOverNodeKey === eventKey && dropPosition === 0;
 
-         const dragOverGapTop = dragOverNodeKey === eventKey && dropPosition === -1;
 
-         const dragOverGapBottom = dragOverNodeKey === eventKey && dropPosition === 1;
 
-         const nodeCls = cls(prefixcls, {
 
-             [`${prefixcls}-level-${level + 1}`]: true,
 
-             [`${prefixcls}-collapsed`]: !expanded,
 
-             [`${prefixcls}-disabled`]: Boolean(disabled),
 
-             [`${prefixcls}-selected`]: selected,
 
-             [`${prefixcls}-active`]: !multiple && active,
 
-             [`${prefixcls}-ellipsis`]: labelEllipsis,
 
-             [`${prefixcls}-filtered`]: filtered && treeNodeFilterProp !== 'label',
 
-             [`${prefixcls}-drag-over`]: !disabled && dragOver,
 
-             [`${prefixcls}-draggable`]: !disabled && draggable && !renderFullLabel,
 
-             // When draggable + renderFullLabel is enabled, the default style
 
-             [`${prefixcls}-fullLabel-draggable`]: !disabled && draggable && renderFullLabel,
 
-             // When draggable + renderFullLabel is turned on, the style of dragover
 
-             [`${prefixcls}-fullLabel-drag-over-gap-top`]: !disabled && dragOverGapTop && renderFullLabel,
 
-             [`${prefixcls}-fullLabel-drag-over-gap-bottom`]: !disabled && dragOverGapBottom && renderFullLabel,
 
-         });
 
-         const labelProps = {
 
-             onClick: this.onClick,
 
-             onContextMenu: this.onContextMenu,
 
-             onDoubleClick: this.onDoubleClick,
 
-             className: nodeCls,
 
-             onExpand: this.onExpand,
 
-             data: rest.data,
 
-             level,
 
-             onCheck: this.onCheck,
 
-             style,
 
-             expandIcon: this.renderArrow(),
 
-             checkStatus: {
 
-                 checked,
 
-                 halfChecked,
 
-             },
 
-             expandStatus: {
 
-                 expanded,
 
-                 loading,
 
-             },
 
-         };
 
-         const dragProps = {
 
-             onDoubleClick: this.onDoubleClick,
 
-             onDragStart: draggable ? this.onDragStart : undefined,
 
-             onDragEnter: draggable ? this.onDragEnter : undefined,
 
-             onDragOver: draggable ? this.onDragOver : undefined,
 
-             onDragLeave: draggable ? this.onDragLeave : undefined,
 
-             onDrop: draggable ? this.onDrop : undefined,
 
-             onDragEnd: draggable ? this.onDragEnd : undefined,
 
-             draggable: (!disabled && draggable) || undefined,
 
-         };
 
-         if (renderFullLabel) {
 
-             const customLabel = renderFullLabel({ ...labelProps });
 
-             if (draggable) {
 
-                 return React.cloneElement(customLabel, {
 
-                     ref: this.setRef,
 
-                     ...dragProps
 
-                 });
 
-             } else {
 
-                 return customLabel;
 
-             }
 
-         }
 
-         const labelCls = cls(`${prefixcls}-label`, {
 
-             [`${prefixcls}-drag-over-gap-top`]: !disabled && dragOverGapTop,
 
-             [`${prefixcls}-drag-over-gap-bottom`]: !disabled && dragOverGapBottom,
 
-         });
 
-         return (
 
-             <li
 
-                 className={nodeCls}
 
-                 role="treenode"
 
-                 data-key={eventKey}
 
-                 onClick={this.onClick}
 
-                 onContextMenu={this.onContextMenu}
 
-                 onDoubleClick={this.onDoubleClick}
 
-                 ref={this.setRef}
 
-                 style={style}
 
-                 {...dragProps}
 
-             >
 
-                 {this.renderArrow()}
 
-                 <span
 
-                     className={labelCls}
 
-                 >
 
-                     {multiple ? this.renderCheckbox() : null}
 
-                     {this.renderIcon()}
 
-                     <span className={`${prefixcls}-label-text`}>{this.renderRealLabel()}</span>
 
-                 </span>
 
-             </li>
 
-         );
 
-     }
 
- }
 
 
  |