354 lines
16 KiB
JavaScript
354 lines
16 KiB
JavaScript
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
|
|
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
|
|
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
|
|
var _excluded = ["eventKey", "className", "style", "dragOver", "dragOverGapTop", "dragOverGapBottom", "isLeaf", "isStart", "isEnd", "expanded", "selected", "checked", "halfChecked", "loading", "domRef", "active", "data", "onMouseMove", "selectable"];
|
|
import React from 'react';
|
|
import classNames from 'classnames';
|
|
import pickAttrs from "rc-util/es/pickAttrs";
|
|
import { TreeContext, UnstableContext } from "./contextTypes";
|
|
import Indent from "./Indent";
|
|
import getEntity from "./utils/keyUtil";
|
|
import { convertNodePropsToEventData } from "./utils/treeUtil";
|
|
var ICON_OPEN = 'open';
|
|
var ICON_CLOSE = 'close';
|
|
var defaultTitle = '---';
|
|
var TreeNode = function TreeNode(props) {
|
|
var _unstableContext$node, _context$filterTreeNo, _classNames4;
|
|
var eventKey = props.eventKey,
|
|
className = props.className,
|
|
style = props.style,
|
|
dragOver = props.dragOver,
|
|
dragOverGapTop = props.dragOverGapTop,
|
|
dragOverGapBottom = props.dragOverGapBottom,
|
|
isLeaf = props.isLeaf,
|
|
isStart = props.isStart,
|
|
isEnd = props.isEnd,
|
|
expanded = props.expanded,
|
|
selected = props.selected,
|
|
checked = props.checked,
|
|
halfChecked = props.halfChecked,
|
|
loading = props.loading,
|
|
domRef = props.domRef,
|
|
active = props.active,
|
|
data = props.data,
|
|
onMouseMove = props.onMouseMove,
|
|
selectable = props.selectable,
|
|
otherProps = _objectWithoutProperties(props, _excluded);
|
|
var context = React.useContext(TreeContext);
|
|
var unstableContext = React.useContext(UnstableContext);
|
|
var selectHandleRef = React.useRef(null);
|
|
var _React$useState = React.useState(false),
|
|
_React$useState2 = _slicedToArray(_React$useState, 2),
|
|
dragNodeHighlight = _React$useState2[0],
|
|
setDragNodeHighlight = _React$useState2[1];
|
|
|
|
// ======= State: Disabled State =======
|
|
var isDisabled = !!(context.disabled || props.disabled || (_unstableContext$node = unstableContext.nodeDisabled) !== null && _unstableContext$node !== void 0 && _unstableContext$node.call(unstableContext, data));
|
|
var isCheckable = React.useMemo(function () {
|
|
// Return false if tree or treeNode is not checkable
|
|
if (!context.checkable || props.checkable === false) {
|
|
return false;
|
|
}
|
|
return context.checkable;
|
|
}, [context.checkable, props.checkable]);
|
|
|
|
// ======= Event Handlers: Selection and Check =======
|
|
var onSelect = function onSelect(e) {
|
|
if (isDisabled) {
|
|
return;
|
|
}
|
|
context.onNodeSelect(e, convertNodePropsToEventData(props));
|
|
};
|
|
var onCheck = function onCheck(e) {
|
|
if (isDisabled) {
|
|
return;
|
|
}
|
|
if (!isCheckable || props.disableCheckbox) {
|
|
return;
|
|
}
|
|
context.onNodeCheck(e, convertNodePropsToEventData(props), !checked);
|
|
};
|
|
|
|
// ======= State: Selectable Check =======
|
|
var isSelectable = React.useMemo(function () {
|
|
// Ignore when selectable is undefined or null
|
|
if (typeof selectable === 'boolean') {
|
|
return selectable;
|
|
}
|
|
return context.selectable;
|
|
}, [selectable, context.selectable]);
|
|
var onSelectorClick = function onSelectorClick(e) {
|
|
// Click trigger before select/check operation
|
|
context.onNodeClick(e, convertNodePropsToEventData(props));
|
|
if (isSelectable) {
|
|
onSelect(e);
|
|
} else {
|
|
onCheck(e);
|
|
}
|
|
};
|
|
var onSelectorDoubleClick = function onSelectorDoubleClick(e) {
|
|
context.onNodeDoubleClick(e, convertNodePropsToEventData(props));
|
|
};
|
|
var onMouseEnter = function onMouseEnter(e) {
|
|
context.onNodeMouseEnter(e, convertNodePropsToEventData(props));
|
|
};
|
|
var onMouseLeave = function onMouseLeave(e) {
|
|
context.onNodeMouseLeave(e, convertNodePropsToEventData(props));
|
|
};
|
|
var onContextMenu = function onContextMenu(e) {
|
|
context.onNodeContextMenu(e, convertNodePropsToEventData(props));
|
|
};
|
|
|
|
// ======= Drag: Drag Enabled =======
|
|
var isDraggable = React.useMemo(function () {
|
|
return !!(context.draggable && (!context.draggable.nodeDraggable || context.draggable.nodeDraggable(data)));
|
|
}, [context.draggable, data]);
|
|
|
|
// ======= Drag: Drag Event Handlers =======
|
|
var onDragStart = function onDragStart(e) {
|
|
e.stopPropagation();
|
|
setDragNodeHighlight(true);
|
|
context.onNodeDragStart(e, props);
|
|
try {
|
|
// ie throw error
|
|
// firefox-need-it
|
|
e.dataTransfer.setData('text/plain', '');
|
|
} catch (_unused) {
|
|
// empty
|
|
}
|
|
};
|
|
var onDragEnter = function onDragEnter(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
context.onNodeDragEnter(e, props);
|
|
};
|
|
var onDragOver = function onDragOver(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
context.onNodeDragOver(e, props);
|
|
};
|
|
var onDragLeave = function onDragLeave(e) {
|
|
e.stopPropagation();
|
|
context.onNodeDragLeave(e, props);
|
|
};
|
|
var onDragEnd = function onDragEnd(e) {
|
|
e.stopPropagation();
|
|
setDragNodeHighlight(false);
|
|
context.onNodeDragEnd(e, props);
|
|
};
|
|
var onDrop = function onDrop(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
setDragNodeHighlight(false);
|
|
context.onNodeDrop(e, props);
|
|
};
|
|
|
|
// ======= Expand: Node Expansion =======
|
|
var onExpand = function onExpand(e) {
|
|
if (loading) {
|
|
return;
|
|
}
|
|
context.onNodeExpand(e, convertNodePropsToEventData(props));
|
|
};
|
|
|
|
// ======= State: Has Children =======
|
|
var hasChildren = React.useMemo(function () {
|
|
var _ref = getEntity(context.keyEntities, eventKey) || {},
|
|
children = _ref.children;
|
|
return Boolean((children || []).length);
|
|
}, [context.keyEntities, eventKey]);
|
|
|
|
// ======= State: Leaf Check =======
|
|
var memoizedIsLeaf = React.useMemo(function () {
|
|
if (isLeaf === false) {
|
|
return false;
|
|
}
|
|
return isLeaf || !context.loadData && !hasChildren || context.loadData && props.loaded && !hasChildren;
|
|
}, [isLeaf, context.loadData, hasChildren, props.loaded]);
|
|
|
|
// ============== Effect ==============
|
|
React.useEffect(function () {
|
|
// Load data to avoid default expanded tree without data
|
|
if (loading) {
|
|
return;
|
|
}
|
|
// read from state to avoid loadData at same time
|
|
if (typeof context.loadData === 'function' && expanded && !memoizedIsLeaf && !props.loaded) {
|
|
// We needn't reload data when has children in sync logic
|
|
// It's only needed in node expanded
|
|
context.onNodeLoad(convertNodePropsToEventData(props));
|
|
}
|
|
}, [loading, context.loadData, context.onNodeLoad, expanded, memoizedIsLeaf, props]);
|
|
|
|
// ==================== Render: Drag Handler ====================
|
|
var dragHandlerNode = React.useMemo(function () {
|
|
var _context$draggable;
|
|
if (!((_context$draggable = context.draggable) !== null && _context$draggable !== void 0 && _context$draggable.icon)) {
|
|
return null;
|
|
}
|
|
return /*#__PURE__*/React.createElement("span", {
|
|
className: "".concat(context.prefixCls, "-draggable-icon")
|
|
}, context.draggable.icon);
|
|
}, [context.draggable]);
|
|
|
|
// ====================== Render: Switcher ======================
|
|
var renderSwitcherIconDom = function renderSwitcherIconDom(isInternalLeaf) {
|
|
var switcherIcon = props.switcherIcon || context.switcherIcon;
|
|
// if switcherIconDom is null, no render switcher span
|
|
if (typeof switcherIcon === 'function') {
|
|
return switcherIcon(_objectSpread(_objectSpread({}, props), {}, {
|
|
isLeaf: isInternalLeaf
|
|
}));
|
|
}
|
|
return switcherIcon;
|
|
};
|
|
|
|
// Switcher
|
|
var renderSwitcher = function renderSwitcher() {
|
|
if (memoizedIsLeaf) {
|
|
// if switcherIconDom is null, no render switcher span
|
|
var _switcherIconDom = renderSwitcherIconDom(true);
|
|
return _switcherIconDom !== false ? /*#__PURE__*/React.createElement("span", {
|
|
className: classNames("".concat(context.prefixCls, "-switcher"), "".concat(context.prefixCls, "-switcher-noop"))
|
|
}, _switcherIconDom) : null;
|
|
}
|
|
var switcherIconDom = renderSwitcherIconDom(false);
|
|
return switcherIconDom !== false ? /*#__PURE__*/React.createElement("span", {
|
|
onClick: onExpand,
|
|
className: classNames("".concat(context.prefixCls, "-switcher"), "".concat(context.prefixCls, "-switcher_").concat(expanded ? ICON_OPEN : ICON_CLOSE))
|
|
}, switcherIconDom) : null;
|
|
};
|
|
|
|
// ====================== Checkbox ======================
|
|
var checkboxNode = React.useMemo(function () {
|
|
if (!isCheckable) {
|
|
return null;
|
|
}
|
|
|
|
// [Legacy] Custom element should be separate with `checkable` in future
|
|
var $custom = typeof isCheckable !== 'boolean' ? isCheckable : null;
|
|
return /*#__PURE__*/React.createElement("span", {
|
|
className: classNames("".concat(context.prefixCls, "-checkbox"), _defineProperty(_defineProperty(_defineProperty({}, "".concat(context.prefixCls, "-checkbox-checked"), checked), "".concat(context.prefixCls, "-checkbox-indeterminate"), !checked && halfChecked), "".concat(context.prefixCls, "-checkbox-disabled"), isDisabled || props.disableCheckbox)),
|
|
onClick: onCheck,
|
|
role: "checkbox",
|
|
"aria-checked": halfChecked ? 'mixed' : checked,
|
|
"aria-disabled": isDisabled || props.disableCheckbox,
|
|
"aria-label": "Select ".concat(typeof props.title === 'string' ? props.title : 'tree node')
|
|
}, $custom);
|
|
}, [isCheckable, checked, halfChecked, isDisabled, props.disableCheckbox, props.title]);
|
|
|
|
// ============== State: Node State (Open/Close) ==============
|
|
var nodeState = React.useMemo(function () {
|
|
if (memoizedIsLeaf) {
|
|
return null;
|
|
}
|
|
return expanded ? ICON_OPEN : ICON_CLOSE;
|
|
}, [memoizedIsLeaf, expanded]);
|
|
|
|
// ==================== Render: Title + Icon ====================
|
|
var iconNode = React.useMemo(function () {
|
|
return /*#__PURE__*/React.createElement("span", {
|
|
className: classNames("".concat(context.prefixCls, "-iconEle"), "".concat(context.prefixCls, "-icon__").concat(nodeState || 'docu'), _defineProperty({}, "".concat(context.prefixCls, "-icon_loading"), loading))
|
|
});
|
|
}, [context.prefixCls, nodeState, loading]);
|
|
|
|
// =================== Drop Indicator ===================
|
|
var dropIndicatorNode = React.useMemo(function () {
|
|
var rootDraggable = Boolean(context.draggable);
|
|
// allowDrop is calculated in Tree.tsx, there is no need for calc it here
|
|
var showIndicator = !props.disabled && rootDraggable && context.dragOverNodeKey === eventKey;
|
|
if (!showIndicator) {
|
|
return null;
|
|
}
|
|
return context.dropIndicatorRender({
|
|
dropPosition: context.dropPosition,
|
|
dropLevelOffset: context.dropLevelOffset,
|
|
indent: context.indent,
|
|
prefixCls: context.prefixCls,
|
|
direction: context.direction
|
|
});
|
|
}, [context.dropPosition, context.dropLevelOffset, context.indent, context.prefixCls, context.direction, context.draggable, context.dragOverNodeKey, context.dropIndicatorRender]);
|
|
|
|
// Icon + Title
|
|
var selectorNode = React.useMemo(function () {
|
|
var _props$title = props.title,
|
|
title = _props$title === void 0 ? defaultTitle : _props$title;
|
|
var wrapClass = "".concat(context.prefixCls, "-node-content-wrapper");
|
|
|
|
// Icon - Still show loading icon when loading without showIcon
|
|
var $icon;
|
|
if (context.showIcon) {
|
|
var currentIcon = props.icon || context.icon;
|
|
$icon = currentIcon ? /*#__PURE__*/React.createElement("span", {
|
|
className: classNames("".concat(context.prefixCls, "-iconEle"), "".concat(context.prefixCls, "-icon__customize"))
|
|
}, typeof currentIcon === 'function' ? currentIcon(props) : currentIcon) : iconNode;
|
|
} else if (context.loadData && loading) {
|
|
$icon = iconNode;
|
|
}
|
|
|
|
// Title
|
|
var titleNode;
|
|
if (typeof title === 'function') {
|
|
titleNode = title(data);
|
|
} else if (context.titleRender) {
|
|
titleNode = context.titleRender(data);
|
|
} else {
|
|
titleNode = title;
|
|
}
|
|
return /*#__PURE__*/React.createElement("span", {
|
|
ref: selectHandleRef,
|
|
title: typeof title === 'string' ? title : '',
|
|
className: classNames(wrapClass, "".concat(wrapClass, "-").concat(nodeState || 'normal'), _defineProperty({}, "".concat(context.prefixCls, "-node-selected"), !isDisabled && (selected || dragNodeHighlight))),
|
|
onMouseEnter: onMouseEnter,
|
|
onMouseLeave: onMouseLeave,
|
|
onContextMenu: onContextMenu,
|
|
onClick: onSelectorClick,
|
|
onDoubleClick: onSelectorDoubleClick
|
|
}, $icon, /*#__PURE__*/React.createElement("span", {
|
|
className: "".concat(context.prefixCls, "-title")
|
|
}, titleNode), dropIndicatorNode);
|
|
}, [context.prefixCls, context.showIcon, props, context.icon, iconNode, context.titleRender, data, nodeState, onMouseEnter, onMouseLeave, onContextMenu, onSelectorClick, onSelectorDoubleClick]);
|
|
var dataOrAriaAttributeProps = pickAttrs(otherProps, {
|
|
aria: true,
|
|
data: true
|
|
});
|
|
var _ref2 = getEntity(context.keyEntities, eventKey) || {},
|
|
level = _ref2.level;
|
|
var isEndNode = isEnd[isEnd.length - 1];
|
|
var draggableWithoutDisabled = !isDisabled && isDraggable;
|
|
var dragging = context.draggingNodeKey === eventKey;
|
|
var ariaSelected = selectable !== undefined ? {
|
|
'aria-selected': !!selectable
|
|
} : undefined;
|
|
return /*#__PURE__*/React.createElement("div", _extends({
|
|
ref: domRef,
|
|
role: "treeitem",
|
|
"aria-expanded": isLeaf ? undefined : expanded,
|
|
className: classNames(className, "".concat(context.prefixCls, "-treenode"), (_classNames4 = {}, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_classNames4, "".concat(context.prefixCls, "-treenode-disabled"), isDisabled), "".concat(context.prefixCls, "-treenode-switcher-").concat(expanded ? 'open' : 'close'), !isLeaf), "".concat(context.prefixCls, "-treenode-checkbox-checked"), checked), "".concat(context.prefixCls, "-treenode-checkbox-indeterminate"), halfChecked), "".concat(context.prefixCls, "-treenode-selected"), selected), "".concat(context.prefixCls, "-treenode-loading"), loading), "".concat(context.prefixCls, "-treenode-active"), active), "".concat(context.prefixCls, "-treenode-leaf-last"), isEndNode), "".concat(context.prefixCls, "-treenode-draggable"), isDraggable), "dragging", dragging), _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_classNames4, 'drop-target', context.dropTargetKey === eventKey), 'drop-container', context.dropContainerKey === eventKey), 'drag-over', !isDisabled && dragOver), 'drag-over-gap-top', !isDisabled && dragOverGapTop), 'drag-over-gap-bottom', !isDisabled && dragOverGapBottom), 'filter-node', (_context$filterTreeNo = context.filterTreeNode) === null || _context$filterTreeNo === void 0 ? void 0 : _context$filterTreeNo.call(context, convertNodePropsToEventData(props))), "".concat(context.prefixCls, "-treenode-leaf"), memoizedIsLeaf))),
|
|
style: style
|
|
// Draggable config
|
|
,
|
|
draggable: draggableWithoutDisabled,
|
|
onDragStart: draggableWithoutDisabled ? onDragStart : undefined
|
|
// Drop config
|
|
,
|
|
onDragEnter: isDraggable ? onDragEnter : undefined,
|
|
onDragOver: isDraggable ? onDragOver : undefined,
|
|
onDragLeave: isDraggable ? onDragLeave : undefined,
|
|
onDrop: isDraggable ? onDrop : undefined,
|
|
onDragEnd: isDraggable ? onDragEnd : undefined,
|
|
onMouseMove: onMouseMove
|
|
}, ariaSelected, dataOrAriaAttributeProps), /*#__PURE__*/React.createElement(Indent, {
|
|
prefixCls: context.prefixCls,
|
|
level: level,
|
|
isStart: isStart,
|
|
isEnd: isEnd
|
|
}), dragHandlerNode, renderSwitcher(), checkboxNode, selectorNode);
|
|
};
|
|
TreeNode.isTreeNode = 1;
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
TreeNode.displayName = 'TreeNode';
|
|
}
|
|
export default TreeNode; |