TreeNode.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. exports.default = void 0;
  7. var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
  8. var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
  9. var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
  10. var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
  11. var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
  12. var _react = _interopRequireDefault(require("react"));
  13. var _classnames = _interopRequireDefault(require("classnames"));
  14. var _pickAttrs = _interopRequireDefault(require("rc-util/lib/pickAttrs"));
  15. var _contextTypes = require("./contextTypes");
  16. var _Indent = _interopRequireDefault(require("./Indent"));
  17. var _keyUtil = _interopRequireDefault(require("./utils/keyUtil"));
  18. var _treeUtil = require("./utils/treeUtil");
  19. var _excluded = ["eventKey", "className", "style", "dragOver", "dragOverGapTop", "dragOverGapBottom", "isLeaf", "isStart", "isEnd", "expanded", "selected", "checked", "halfChecked", "loading", "domRef", "active", "data", "onMouseMove", "selectable"];
  20. var ICON_OPEN = 'open';
  21. var ICON_CLOSE = 'close';
  22. var defaultTitle = '---';
  23. var TreeNode = function TreeNode(props) {
  24. var _unstableContext$node, _context$filterTreeNo, _classNames4;
  25. var eventKey = props.eventKey,
  26. className = props.className,
  27. style = props.style,
  28. dragOver = props.dragOver,
  29. dragOverGapTop = props.dragOverGapTop,
  30. dragOverGapBottom = props.dragOverGapBottom,
  31. isLeaf = props.isLeaf,
  32. isStart = props.isStart,
  33. isEnd = props.isEnd,
  34. expanded = props.expanded,
  35. selected = props.selected,
  36. checked = props.checked,
  37. halfChecked = props.halfChecked,
  38. loading = props.loading,
  39. domRef = props.domRef,
  40. active = props.active,
  41. data = props.data,
  42. onMouseMove = props.onMouseMove,
  43. selectable = props.selectable,
  44. otherProps = (0, _objectWithoutProperties2.default)(props, _excluded);
  45. var context = _react.default.useContext(_contextTypes.TreeContext);
  46. var unstableContext = _react.default.useContext(_contextTypes.UnstableContext);
  47. var selectHandleRef = _react.default.useRef(null);
  48. var _React$useState = _react.default.useState(false),
  49. _React$useState2 = (0, _slicedToArray2.default)(_React$useState, 2),
  50. dragNodeHighlight = _React$useState2[0],
  51. setDragNodeHighlight = _React$useState2[1];
  52. // ======= State: Disabled State =======
  53. var isDisabled = !!(context.disabled || props.disabled || (_unstableContext$node = unstableContext.nodeDisabled) !== null && _unstableContext$node !== void 0 && _unstableContext$node.call(unstableContext, data));
  54. var isCheckable = _react.default.useMemo(function () {
  55. // Return false if tree or treeNode is not checkable
  56. if (!context.checkable || props.checkable === false) {
  57. return false;
  58. }
  59. return context.checkable;
  60. }, [context.checkable, props.checkable]);
  61. // ======= Event Handlers: Selection and Check =======
  62. var onSelect = function onSelect(e) {
  63. if (isDisabled) {
  64. return;
  65. }
  66. context.onNodeSelect(e, (0, _treeUtil.convertNodePropsToEventData)(props));
  67. };
  68. var onCheck = function onCheck(e) {
  69. if (isDisabled) {
  70. return;
  71. }
  72. if (!isCheckable || props.disableCheckbox) {
  73. return;
  74. }
  75. context.onNodeCheck(e, (0, _treeUtil.convertNodePropsToEventData)(props), !checked);
  76. };
  77. // ======= State: Selectable Check =======
  78. var isSelectable = _react.default.useMemo(function () {
  79. // Ignore when selectable is undefined or null
  80. if (typeof selectable === 'boolean') {
  81. return selectable;
  82. }
  83. return context.selectable;
  84. }, [selectable, context.selectable]);
  85. var onSelectorClick = function onSelectorClick(e) {
  86. // Click trigger before select/check operation
  87. context.onNodeClick(e, (0, _treeUtil.convertNodePropsToEventData)(props));
  88. if (isSelectable) {
  89. onSelect(e);
  90. } else {
  91. onCheck(e);
  92. }
  93. };
  94. var onSelectorDoubleClick = function onSelectorDoubleClick(e) {
  95. context.onNodeDoubleClick(e, (0, _treeUtil.convertNodePropsToEventData)(props));
  96. };
  97. var onMouseEnter = function onMouseEnter(e) {
  98. context.onNodeMouseEnter(e, (0, _treeUtil.convertNodePropsToEventData)(props));
  99. };
  100. var onMouseLeave = function onMouseLeave(e) {
  101. context.onNodeMouseLeave(e, (0, _treeUtil.convertNodePropsToEventData)(props));
  102. };
  103. var onContextMenu = function onContextMenu(e) {
  104. context.onNodeContextMenu(e, (0, _treeUtil.convertNodePropsToEventData)(props));
  105. };
  106. // ======= Drag: Drag Enabled =======
  107. var isDraggable = _react.default.useMemo(function () {
  108. return !!(context.draggable && (!context.draggable.nodeDraggable || context.draggable.nodeDraggable(data)));
  109. }, [context.draggable, data]);
  110. // ======= Drag: Drag Event Handlers =======
  111. var onDragStart = function onDragStart(e) {
  112. e.stopPropagation();
  113. setDragNodeHighlight(true);
  114. context.onNodeDragStart(e, props);
  115. try {
  116. // ie throw error
  117. // firefox-need-it
  118. e.dataTransfer.setData('text/plain', '');
  119. } catch (_unused) {
  120. // empty
  121. }
  122. };
  123. var onDragEnter = function onDragEnter(e) {
  124. e.preventDefault();
  125. e.stopPropagation();
  126. context.onNodeDragEnter(e, props);
  127. };
  128. var onDragOver = function onDragOver(e) {
  129. e.preventDefault();
  130. e.stopPropagation();
  131. context.onNodeDragOver(e, props);
  132. };
  133. var onDragLeave = function onDragLeave(e) {
  134. e.stopPropagation();
  135. context.onNodeDragLeave(e, props);
  136. };
  137. var onDragEnd = function onDragEnd(e) {
  138. e.stopPropagation();
  139. setDragNodeHighlight(false);
  140. context.onNodeDragEnd(e, props);
  141. };
  142. var onDrop = function onDrop(e) {
  143. e.preventDefault();
  144. e.stopPropagation();
  145. setDragNodeHighlight(false);
  146. context.onNodeDrop(e, props);
  147. };
  148. // ======= Expand: Node Expansion =======
  149. var onExpand = function onExpand(e) {
  150. if (loading) {
  151. return;
  152. }
  153. context.onNodeExpand(e, (0, _treeUtil.convertNodePropsToEventData)(props));
  154. };
  155. // ======= State: Has Children =======
  156. var hasChildren = _react.default.useMemo(function () {
  157. var _ref = (0, _keyUtil.default)(context.keyEntities, eventKey) || {},
  158. children = _ref.children;
  159. return Boolean((children || []).length);
  160. }, [context.keyEntities, eventKey]);
  161. // ======= State: Leaf Check =======
  162. var memoizedIsLeaf = _react.default.useMemo(function () {
  163. if (isLeaf === false) {
  164. return false;
  165. }
  166. return isLeaf || !context.loadData && !hasChildren || context.loadData && props.loaded && !hasChildren;
  167. }, [isLeaf, context.loadData, hasChildren, props.loaded]);
  168. // ============== Effect ==============
  169. _react.default.useEffect(function () {
  170. // Load data to avoid default expanded tree without data
  171. if (loading) {
  172. return;
  173. }
  174. // read from state to avoid loadData at same time
  175. if (typeof context.loadData === 'function' && expanded && !memoizedIsLeaf && !props.loaded) {
  176. // We needn't reload data when has children in sync logic
  177. // It's only needed in node expanded
  178. context.onNodeLoad((0, _treeUtil.convertNodePropsToEventData)(props));
  179. }
  180. }, [loading, context.loadData, context.onNodeLoad, expanded, memoizedIsLeaf, props]);
  181. // ==================== Render: Drag Handler ====================
  182. var dragHandlerNode = _react.default.useMemo(function () {
  183. var _context$draggable;
  184. if (!((_context$draggable = context.draggable) !== null && _context$draggable !== void 0 && _context$draggable.icon)) {
  185. return null;
  186. }
  187. return /*#__PURE__*/_react.default.createElement("span", {
  188. className: "".concat(context.prefixCls, "-draggable-icon")
  189. }, context.draggable.icon);
  190. }, [context.draggable]);
  191. // ====================== Render: Switcher ======================
  192. var renderSwitcherIconDom = function renderSwitcherIconDom(isInternalLeaf) {
  193. var switcherIcon = props.switcherIcon || context.switcherIcon;
  194. // if switcherIconDom is null, no render switcher span
  195. if (typeof switcherIcon === 'function') {
  196. return switcherIcon((0, _objectSpread2.default)((0, _objectSpread2.default)({}, props), {}, {
  197. isLeaf: isInternalLeaf
  198. }));
  199. }
  200. return switcherIcon;
  201. };
  202. // Switcher
  203. var renderSwitcher = function renderSwitcher() {
  204. if (memoizedIsLeaf) {
  205. // if switcherIconDom is null, no render switcher span
  206. var _switcherIconDom = renderSwitcherIconDom(true);
  207. return _switcherIconDom !== false ? /*#__PURE__*/_react.default.createElement("span", {
  208. className: (0, _classnames.default)("".concat(context.prefixCls, "-switcher"), "".concat(context.prefixCls, "-switcher-noop"))
  209. }, _switcherIconDom) : null;
  210. }
  211. var switcherIconDom = renderSwitcherIconDom(false);
  212. return switcherIconDom !== false ? /*#__PURE__*/_react.default.createElement("span", {
  213. onClick: onExpand,
  214. className: (0, _classnames.default)("".concat(context.prefixCls, "-switcher"), "".concat(context.prefixCls, "-switcher_").concat(expanded ? ICON_OPEN : ICON_CLOSE))
  215. }, switcherIconDom) : null;
  216. };
  217. // ====================== Checkbox ======================
  218. var checkboxNode = _react.default.useMemo(function () {
  219. if (!isCheckable) {
  220. return null;
  221. }
  222. // [Legacy] Custom element should be separate with `checkable` in future
  223. var $custom = typeof isCheckable !== 'boolean' ? isCheckable : null;
  224. return /*#__PURE__*/_react.default.createElement("span", {
  225. className: (0, _classnames.default)("".concat(context.prefixCls, "-checkbox"), (0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)({}, "".concat(context.prefixCls, "-checkbox-checked"), checked), "".concat(context.prefixCls, "-checkbox-indeterminate"), !checked && halfChecked), "".concat(context.prefixCls, "-checkbox-disabled"), isDisabled || props.disableCheckbox)),
  226. onClick: onCheck,
  227. role: "checkbox",
  228. "aria-checked": halfChecked ? 'mixed' : checked,
  229. "aria-disabled": isDisabled || props.disableCheckbox,
  230. "aria-label": "Select ".concat(typeof props.title === 'string' ? props.title : 'tree node')
  231. }, $custom);
  232. }, [isCheckable, checked, halfChecked, isDisabled, props.disableCheckbox, props.title]);
  233. // ============== State: Node State (Open/Close) ==============
  234. var nodeState = _react.default.useMemo(function () {
  235. if (memoizedIsLeaf) {
  236. return null;
  237. }
  238. return expanded ? ICON_OPEN : ICON_CLOSE;
  239. }, [memoizedIsLeaf, expanded]);
  240. // ==================== Render: Title + Icon ====================
  241. var iconNode = _react.default.useMemo(function () {
  242. return /*#__PURE__*/_react.default.createElement("span", {
  243. className: (0, _classnames.default)("".concat(context.prefixCls, "-iconEle"), "".concat(context.prefixCls, "-icon__").concat(nodeState || 'docu'), (0, _defineProperty2.default)({}, "".concat(context.prefixCls, "-icon_loading"), loading))
  244. });
  245. }, [context.prefixCls, nodeState, loading]);
  246. // =================== Drop Indicator ===================
  247. var dropIndicatorNode = _react.default.useMemo(function () {
  248. var rootDraggable = Boolean(context.draggable);
  249. // allowDrop is calculated in Tree.tsx, there is no need for calc it here
  250. var showIndicator = !props.disabled && rootDraggable && context.dragOverNodeKey === eventKey;
  251. if (!showIndicator) {
  252. return null;
  253. }
  254. return context.dropIndicatorRender({
  255. dropPosition: context.dropPosition,
  256. dropLevelOffset: context.dropLevelOffset,
  257. indent: context.indent,
  258. prefixCls: context.prefixCls,
  259. direction: context.direction
  260. });
  261. }, [context.dropPosition, context.dropLevelOffset, context.indent, context.prefixCls, context.direction, context.draggable, context.dragOverNodeKey, context.dropIndicatorRender]);
  262. // Icon + Title
  263. var selectorNode = _react.default.useMemo(function () {
  264. var _props$title = props.title,
  265. title = _props$title === void 0 ? defaultTitle : _props$title;
  266. var wrapClass = "".concat(context.prefixCls, "-node-content-wrapper");
  267. // Icon - Still show loading icon when loading without showIcon
  268. var $icon;
  269. if (context.showIcon) {
  270. var currentIcon = props.icon || context.icon;
  271. $icon = currentIcon ? /*#__PURE__*/_react.default.createElement("span", {
  272. className: (0, _classnames.default)("".concat(context.prefixCls, "-iconEle"), "".concat(context.prefixCls, "-icon__customize"))
  273. }, typeof currentIcon === 'function' ? currentIcon(props) : currentIcon) : iconNode;
  274. } else if (context.loadData && loading) {
  275. $icon = iconNode;
  276. }
  277. // Title
  278. var titleNode;
  279. if (typeof title === 'function') {
  280. titleNode = title(data);
  281. } else if (context.titleRender) {
  282. titleNode = context.titleRender(data);
  283. } else {
  284. titleNode = title;
  285. }
  286. return /*#__PURE__*/_react.default.createElement("span", {
  287. ref: selectHandleRef,
  288. title: typeof title === 'string' ? title : '',
  289. className: (0, _classnames.default)(wrapClass, "".concat(wrapClass, "-").concat(nodeState || 'normal'), (0, _defineProperty2.default)({}, "".concat(context.prefixCls, "-node-selected"), !isDisabled && (selected || dragNodeHighlight))),
  290. onMouseEnter: onMouseEnter,
  291. onMouseLeave: onMouseLeave,
  292. onContextMenu: onContextMenu,
  293. onClick: onSelectorClick,
  294. onDoubleClick: onSelectorDoubleClick
  295. }, $icon, /*#__PURE__*/_react.default.createElement("span", {
  296. className: "".concat(context.prefixCls, "-title")
  297. }, titleNode), dropIndicatorNode);
  298. }, [context.prefixCls, context.showIcon, props, context.icon, iconNode, context.titleRender, data, nodeState, onMouseEnter, onMouseLeave, onContextMenu, onSelectorClick, onSelectorDoubleClick]);
  299. var dataOrAriaAttributeProps = (0, _pickAttrs.default)(otherProps, {
  300. aria: true,
  301. data: true
  302. });
  303. var _ref2 = (0, _keyUtil.default)(context.keyEntities, eventKey) || {},
  304. level = _ref2.level;
  305. var isEndNode = isEnd[isEnd.length - 1];
  306. var draggableWithoutDisabled = !isDisabled && isDraggable;
  307. var dragging = context.draggingNodeKey === eventKey;
  308. var ariaSelected = selectable !== undefined ? {
  309. 'aria-selected': !!selectable
  310. } : undefined;
  311. return /*#__PURE__*/_react.default.createElement("div", (0, _extends2.default)({
  312. ref: domRef,
  313. role: "treeitem",
  314. "aria-expanded": isLeaf ? undefined : expanded,
  315. className: (0, _classnames.default)(className, "".concat(context.prefixCls, "-treenode"), (_classNames4 = {}, (0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)(_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), (0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)(_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, (0, _treeUtil.convertNodePropsToEventData)(props))), "".concat(context.prefixCls, "-treenode-leaf"), memoizedIsLeaf))),
  316. style: style
  317. // Draggable config
  318. ,
  319. draggable: draggableWithoutDisabled,
  320. onDragStart: draggableWithoutDisabled ? onDragStart : undefined
  321. // Drop config
  322. ,
  323. onDragEnter: isDraggable ? onDragEnter : undefined,
  324. onDragOver: isDraggable ? onDragOver : undefined,
  325. onDragLeave: isDraggable ? onDragLeave : undefined,
  326. onDrop: isDraggable ? onDrop : undefined,
  327. onDragEnd: isDraggable ? onDragEnd : undefined,
  328. onMouseMove: onMouseMove
  329. }, ariaSelected, dataOrAriaAttributeProps), /*#__PURE__*/_react.default.createElement(_Indent.default, {
  330. prefixCls: context.prefixCls,
  331. level: level,
  332. isStart: isStart,
  333. isEnd: isEnd
  334. }), dragHandlerNode, renderSwitcher(), checkboxNode, selectorNode);
  335. };
  336. TreeNode.isTreeNode = 1;
  337. if (process.env.NODE_ENV !== 'production') {
  338. TreeNode.displayName = 'TreeNode';
  339. }
  340. var _default = exports.default = TreeNode;