import * as React from "react"; import PropTypes from "prop-types"; import classNames from "classnames"; // import { withStyles } from "@material-ui/styles"; //@ts-ignore import { TreeContext, TreeContextProps } from "./context-types"; import { getDataAndAria } from "./util"; import { IconType, Key, DataNode, NodeInstance } from "./interface"; import { Indent } from "./indent"; import { convertNodePropsToEventData } from "./util/tree-util"; /** * Some basic component styles */ // const styles = { // coTree: { // margin: 0, // border: "1px solid transparent" // }, // coTreeTreenode: { // margin: 0, // padding: 0, // height: "29px", // lineHeight: "20px", // outline: 0, // whiteSpace: "nowrap", // listStyle: "none", // display: "flex", // alignItems: "center", // userSelect: "none", // borderTop: "2px solid transparent", // borderBottom: "2px solid transparent", // "& :hover": { // backgroundColor: "#eee" // }, // dragOver: { // color: "white", // backgroundColor: "#99bcf5", // opacity: 0.7 // }, // dragOverGapTop: { // borderTopColor: "skyblue" // }, // dragOverGapBottom: { // borderBottomColor: "skyblue" // }, // draggable: { // color: "#333", // userSelect: "none" // } // }, // coTreeNodeContentWrapper: { // display: "inline-block", // height: "20px", // margin: 0, // padding: "1px 2px 0 2px", // textDecoration: "none", // verticalAlign: "top", // cursor: "pointer" // }, // coTreeIconEle: { // display: "inline-block", // width: "16px", // height: "16px", // lineHeight: "16px", // verticalAlign: "middle", // border: "none", // outline: "none", // cursor: "pointer" // }, // coTreeSwitcher: { // display: "inline-block", // width: "16px", // height: "16px", // lineHeight: "16px", // verticalAlign: "middle", // border: "none", // outline: "none", // cursor: "pointer", // marginLeft: "20px" // }, // coTreeSwitcher_open: { // background: `url(${"./assets/arrowright.png"}) no-repeat 0 0 transparent`, // transform: "rotate(90deg)", // transition: "0.2s linear" // }, // coTreeSwitcher_close: { // background: `url(${"./assets/arrowright.png"}) no-repeat 0 0 transparent`, // transition: "0.2s linear" // }, // coTreeTreeNodeDisabled: { // span: { // "&:not(coTreeSwitcher)": { // color: "#767676", // cursor: "not-allowed" // } // }, // a: { // color: "#767676", // cursor: "not-allowed", // span: { // color: "#767676", // cursor: "not-allowed" // } // } // }, // coTreeTreenodeActive: { // background: "rgba(19, 18, 18, 0.1)" // }, // coTreeTreenodeSelected: { // backgroundColor: "#e6ecf1", // fontWieght: "bold", // opacity: "0.8" // }, // coTreeIcon__open: { // marginRight: "2px", // marginBottom: "4px", // verticalAlign: "middle", // background: `url(${"./assets/notebookp.png"}) no-repeat 0 0 transparent` // }, // coTreeIcon__close: { // marginRight: "2px", // marginBottom: "4px", // verticalAlign: "middle", // background: `url(${"./assets/notebookp.png"}) no-repeat 0 0 transparent` // }, // coTreeIcon__docu: { // marginRight: "2px", // marginBottom: "4px", // verticalAlign: "middle", // background: `url(${"./assets/notebookc.png"}) no-repeat 0 0 transparent` // }, // coTreeIcon__customize: { // marginRight: "2px", // verticalAlign: "top" // }, // coTreeIndentUnit: { // display: "inline-block", // paddingLeft: "10px" // } // }; const ICON_OPEN = "open"; const ICON_CLOSE = "close"; const defaultTitle = "---"; interface TreeNodeProps { eventKey?: Key; prefixCls?: string; className?: string; style?: React.CSSProperties; expanded?: boolean; selected?: boolean; loaded?: boolean; loading?: boolean; title?: React.ReactNode | ((data: DataNode) => React.ReactNode); dragOver?: boolean; dragOverGapTop?: boolean; dragOverGapBottom?: boolean; pos?: string; domRef?: React.Ref; selectHandle?: React.Ref; //For easy data access data?: DataNode; isStart?: boolean[]; isEnd?: boolean[]; active: boolean; onMouseMove?: React.MouseEventHandler; isTreeNode?: boolean; //Old type isLeaf selectable?: boolean; disabled?: boolean; icon?: IconType; switcherIcon?: IconType; children?: React.ReactNode; onSelect?: (...args: any[]) => any; node: NodeInstance; } interface InternalTreeNodeProps extends TreeNodeProps { context?: TreeContextProps; } const InternalTreeNode: React.FC = props => { InternalTreeNode.propTypes = { prefixCls: PropTypes.string, className: PropTypes.string, style: PropTypes.object, onSelect: PropTypes.func, eventKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), expanded: PropTypes.bool, selected: PropTypes.bool, loaded: PropTypes.bool, loading: PropTypes.bool, title: PropTypes.oneOfType([PropTypes.node, PropTypes.func]), dragOver: PropTypes.bool, dragOverGapTop: PropTypes.bool, dragOverGapBottom: PropTypes.bool, pos: PropTypes.string, isTreeNode: PropTypes.bool, selectable: PropTypes.bool, disabled: PropTypes.bool, icon: PropTypes.oneOfType([PropTypes.node, PropTypes.func]), switcherIcon: PropTypes.oneOfType([PropTypes.node, PropTypes.func]) }; // const [state, setState] = React.useState({ // dragNodeHighlight: false // }); const [dragNodeHighlight, setDragNodeHighlight] = React.useState( false ); const domRef = React.useRef(); const selectHandle = React.useRef(); // let selectHandle: HTMLSpanElement; React.useEffect(() => { syncLoadData(props); }); const onSelectorClick = e => { //Click trigger before select operation const { context: { onNodeClick } } = props; onNodeClick(e, convertNodePropsToEventData(props)); if (isSelectable()) { onSelect(e); } }; const onSelectorDoubleClick = e => { const { context: { onNodeDoubleClick } } = props; onNodeDoubleClick(e, convertNodePropsToEventData(props)); }; const onSelect = e => { if (isDisabled()) return; const { context: { onNodeSelect } } = props; e.preventDefault(); onNodeSelect(e, convertNodePropsToEventData(props)); }; const onMouseEnter = e => { const { context: { onNodeMouseEnter } } = props; onNodeMouseEnter(e, convertNodePropsToEventData(props)); }; const onMouseLeave = e => { const { context: { onNodeMouseLeave } } = props; onNodeMouseLeave(e, convertNodePropsToEventData(props)); }; const onContextMenu = e => { const { context: { onNodeContextMenu } } = props; onNodeContextMenu(e, convertNodePropsToEventData(props)); }; const onDragStart = e => { const { context: { onNodeDragStart } } = props; e.stopPropagation(); setDragNodeHighlight(false); onNodeDragStart(e, props.node); try { // ie throw error // firefox-need-it e.dataTransfer.setData("text/plain", ""); } catch (error) { // empty } }; const onDragEnter = e => { const { context: { onNodeDragEnter } } = props; e.stopPropagation(); onNodeDragEnter(e, props.node); }; const onDragOver = e => { const { context: { onNodeDragOver } } = props; e.preventDefault(); e.stopPropagation(); onNodeDragOver(e, props.node); }; const onDragLeave = e => { const { context: { onNodeDragLeave } } = props; e.stopPropagation(); onNodeDragLeave(e, props.node); }; const onDragEnd = e => { const { context: { onNodeDragEnd } } = props; e.stopPropagation(); setDragNodeHighlight(false); onNodeDragEnd(e, props.node); }; const onDrop = e => { const { context: { onNodeDrop } } = props; e.preventDefault(); e.stopPropagation(); setDragNodeHighlight(false); onNodeDrop(e, props.node); }; //Disable item still can be switch const onExpand = e => { const { context: { onNodeExpand } } = props; onNodeExpand(e, convertNodePropsToEventData(props)); }; // const setSelectHandle = node => { // // (selectHandle: HTMLSpanElement) => selectHandle; // selectHandle = node; // return selectHandle; // }; const getNodeState = () => { const { expanded } = props; if (isLeaf()) return null; return expanded ? ICON_OPEN : ICON_CLOSE; }; const hasChildren = () => { const { eventKey } = props; const { context: { keyEntities } } = props; const { children } = keyEntities[eventKey] || {}; return !!(children || []).length; }; const isLeaf = () => { const { isTreeNode, loaded } = props; const { context: { loadData } } = props; const getChildren = hasChildren(); if (isTreeNode === false) { return false; } return ( isTreeNode || (!loadData && !getChildren) || (loadData && loaded && !getChildren) ); }; const isDisabled = () => { const { disabled } = props; const { context: { disabled: treeDisabled } } = props; return !!(treeDisabled || disabled); }; //Load data to avoid default expanded tree without data const syncLoadData = prop => { const { expanded, loading, loaded } = prop; const { context: { loadData, onNodeLoad } } = props; if (loading) return; //Read from state to avoid loadData at same time if (loadData && expanded && !isLeaf()) { //We needn't reload data when has children in sync logic //It's only needed in node expanded if (!hasChildren() && !loaded) { onNodeLoad(convertNodePropsToEventData(props)); } } }; const isSelectable = () => { const { selectable } = props; const { context: { selectable: treeSelectable } } = props; //Ignore when selectable is undefined or null if (typeof selectable === "boolean") { return selectable; } return treeSelectable; }; //Switcher const renderSwitcher = () => { const { expanded, switcherIcon: switcherIconFromProps } = props; const { context: { prefixCls, switcherIcon: switcherIconFromCtx } } = props; const switcherIcon = switcherIconFromProps || switcherIconFromCtx; if (isLeaf()) { return ( {typeof switcherIcon === "function" ? switcherIcon({ ...props, isTreeNode: true }) : switcherIcon} ); } const switcherCls = classNames( `${prefixCls}-switcher`, `${prefixCls}-switcher_${expanded ? ICON_OPEN : ICON_CLOSE}` ); return ( {typeof switcherIcon === "function" ? switcherIcon({ ...props, isTreeNode: false }) : switcherIcon} ); }; const renderIcon = () => { const { loading } = props; const { context: { prefixCls } } = props; return ( ); }; //Icon + Title const renderSelector = () => { const dragNodeHighlights = dragNodeHighlight; const { title, selected, icon, loading, data } = props; const { context: { prefixCls, showIcon, icon: treeIcon, draggable, loadData } } = props; const disabled = isDisabled(); const wrapClass = `${prefixCls}-node-content-wrapper`; //Icon - still show loading icon when loading without showIcon let $icon; if (showIcon) { const currentIcon = icon || treeIcon; $icon = currentIcon ? ( {typeof currentIcon === "function" ? currentIcon(props) : currentIcon} ) : ( renderIcon() ); } else if (loadData && loading) { $icon = renderIcon(); } //Title const $title = ( {typeof title === "function" ? title(data) : title} ); return ( {$icon} {$title} ); }; const { eventKey, className, style, dragOver, dragOverGapTop, dragOverGapBottom, isTreeNode, isStart, isEnd, expanded, selected, loading, // domRef, active, onMouseMove, ...otherProps } = props; const { context: { prefixCls, filterTreeNode, draggable, keyEntities } } = props; const disabled = isDisabled(); const dataOrAriaAttributeProps = getDataAndAria(otherProps); const { level } = keyEntities[eventKey] || {}; return (
{renderSwitcher()} {renderSelector()}
); }; const ContextTreeNode: React.FC = props => { const context = React.useContext(TreeContext); return ; }; ContextTreeNode.displayName = "TreeNode"; ContextTreeNode.defaultProps = { title: defaultTitle }; (ContextTreeNode as any).isTreeNode = 1; export { TreeNodeProps, InternalTreeNode, InternalTreeNodeProps }; export default ContextTreeNode;