NodeList.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. import _extends from "@babel/runtime/helpers/esm/extends";
  2. import _objectDestructuringEmpty from "@babel/runtime/helpers/esm/objectDestructuringEmpty";
  3. import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
  4. import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
  5. var _excluded = ["prefixCls", "data", "selectable", "checkable", "expandedKeys", "selectedKeys", "checkedKeys", "loadedKeys", "loadingKeys", "halfCheckedKeys", "keyEntities", "disabled", "dragging", "dragOverNodeKey", "dropPosition", "motion", "height", "itemHeight", "virtual", "scrollWidth", "focusable", "activeItem", "focused", "tabIndex", "onKeyDown", "onFocus", "onBlur", "onActiveChange", "onListChangeStart", "onListChangeEnd"];
  6. /**
  7. * Handle virtual list of the TreeNodes.
  8. */
  9. import useLayoutEffect from "rc-util/es/hooks/useLayoutEffect";
  10. import VirtualList from 'rc-virtual-list';
  11. import * as React from 'react';
  12. import MotionTreeNode from "./MotionTreeNode";
  13. import { findExpandedKeys, getExpandRange } from "./utils/diffUtil";
  14. import { getKey, getTreeNodeProps } from "./utils/treeUtil";
  15. var HIDDEN_STYLE = {
  16. width: 0,
  17. height: 0,
  18. display: 'flex',
  19. overflow: 'hidden',
  20. opacity: 0,
  21. border: 0,
  22. padding: 0,
  23. margin: 0
  24. };
  25. var noop = function noop() {};
  26. export var MOTION_KEY = "RC_TREE_MOTION_".concat(Math.random());
  27. var MotionNode = {
  28. key: MOTION_KEY
  29. };
  30. export var MotionEntity = {
  31. key: MOTION_KEY,
  32. level: 0,
  33. index: 0,
  34. pos: '0',
  35. node: MotionNode,
  36. nodes: [MotionNode]
  37. };
  38. var MotionFlattenData = {
  39. parent: null,
  40. children: [],
  41. pos: MotionEntity.pos,
  42. data: MotionNode,
  43. title: null,
  44. key: MOTION_KEY,
  45. /** Hold empty list here since we do not use it */
  46. isStart: [],
  47. isEnd: []
  48. };
  49. /**
  50. * We only need get visible content items to play the animation.
  51. */
  52. export function getMinimumRangeTransitionRange(list, virtual, height, itemHeight) {
  53. if (virtual === false || !height) {
  54. return list;
  55. }
  56. return list.slice(0, Math.ceil(height / itemHeight) + 1);
  57. }
  58. function itemKey(item) {
  59. var key = item.key,
  60. pos = item.pos;
  61. return getKey(key, pos);
  62. }
  63. function getAccessibilityPath(item) {
  64. var path = String(item.data.key);
  65. var current = item;
  66. while (current.parent) {
  67. current = current.parent;
  68. path = "".concat(current.data.key, " > ").concat(path);
  69. }
  70. return path;
  71. }
  72. var NodeList = /*#__PURE__*/React.forwardRef(function (props, ref) {
  73. var prefixCls = props.prefixCls,
  74. data = props.data,
  75. selectable = props.selectable,
  76. checkable = props.checkable,
  77. expandedKeys = props.expandedKeys,
  78. selectedKeys = props.selectedKeys,
  79. checkedKeys = props.checkedKeys,
  80. loadedKeys = props.loadedKeys,
  81. loadingKeys = props.loadingKeys,
  82. halfCheckedKeys = props.halfCheckedKeys,
  83. keyEntities = props.keyEntities,
  84. disabled = props.disabled,
  85. dragging = props.dragging,
  86. dragOverNodeKey = props.dragOverNodeKey,
  87. dropPosition = props.dropPosition,
  88. motion = props.motion,
  89. height = props.height,
  90. itemHeight = props.itemHeight,
  91. virtual = props.virtual,
  92. scrollWidth = props.scrollWidth,
  93. focusable = props.focusable,
  94. activeItem = props.activeItem,
  95. focused = props.focused,
  96. tabIndex = props.tabIndex,
  97. onKeyDown = props.onKeyDown,
  98. onFocus = props.onFocus,
  99. onBlur = props.onBlur,
  100. onActiveChange = props.onActiveChange,
  101. onListChangeStart = props.onListChangeStart,
  102. onListChangeEnd = props.onListChangeEnd,
  103. domProps = _objectWithoutProperties(props, _excluded);
  104. // =============================== Ref ================================
  105. var listRef = React.useRef(null);
  106. var indentMeasurerRef = React.useRef(null);
  107. React.useImperativeHandle(ref, function () {
  108. return {
  109. scrollTo: function scrollTo(scroll) {
  110. listRef.current.scrollTo(scroll);
  111. },
  112. getIndentWidth: function getIndentWidth() {
  113. return indentMeasurerRef.current.offsetWidth;
  114. }
  115. };
  116. });
  117. // ============================== Motion ==============================
  118. var _React$useState = React.useState(expandedKeys),
  119. _React$useState2 = _slicedToArray(_React$useState, 2),
  120. prevExpandedKeys = _React$useState2[0],
  121. setPrevExpandedKeys = _React$useState2[1];
  122. var _React$useState3 = React.useState(data),
  123. _React$useState4 = _slicedToArray(_React$useState3, 2),
  124. prevData = _React$useState4[0],
  125. setPrevData = _React$useState4[1];
  126. var _React$useState5 = React.useState(data),
  127. _React$useState6 = _slicedToArray(_React$useState5, 2),
  128. transitionData = _React$useState6[0],
  129. setTransitionData = _React$useState6[1];
  130. var _React$useState7 = React.useState([]),
  131. _React$useState8 = _slicedToArray(_React$useState7, 2),
  132. transitionRange = _React$useState8[0],
  133. setTransitionRange = _React$useState8[1];
  134. var _React$useState9 = React.useState(null),
  135. _React$useState10 = _slicedToArray(_React$useState9, 2),
  136. motionType = _React$useState10[0],
  137. setMotionType = _React$useState10[1];
  138. // When motion end but data change, this will makes data back to previous one
  139. var dataRef = React.useRef(data);
  140. dataRef.current = data;
  141. function onMotionEnd() {
  142. var latestData = dataRef.current;
  143. setPrevData(latestData);
  144. setTransitionData(latestData);
  145. setTransitionRange([]);
  146. setMotionType(null);
  147. onListChangeEnd();
  148. }
  149. // Do animation if expanded keys changed
  150. // layoutEffect here to avoid blink of node removing
  151. useLayoutEffect(function () {
  152. setPrevExpandedKeys(expandedKeys);
  153. var diffExpanded = findExpandedKeys(prevExpandedKeys, expandedKeys);
  154. if (diffExpanded.key !== null) {
  155. if (diffExpanded.add) {
  156. var keyIndex = prevData.findIndex(function (_ref) {
  157. var key = _ref.key;
  158. return key === diffExpanded.key;
  159. });
  160. var rangeNodes = getMinimumRangeTransitionRange(getExpandRange(prevData, data, diffExpanded.key), virtual, height, itemHeight);
  161. var newTransitionData = prevData.slice();
  162. newTransitionData.splice(keyIndex + 1, 0, MotionFlattenData);
  163. setTransitionData(newTransitionData);
  164. setTransitionRange(rangeNodes);
  165. setMotionType('show');
  166. } else {
  167. var _keyIndex = data.findIndex(function (_ref2) {
  168. var key = _ref2.key;
  169. return key === diffExpanded.key;
  170. });
  171. var _rangeNodes = getMinimumRangeTransitionRange(getExpandRange(data, prevData, diffExpanded.key), virtual, height, itemHeight);
  172. var _newTransitionData = data.slice();
  173. _newTransitionData.splice(_keyIndex + 1, 0, MotionFlattenData);
  174. setTransitionData(_newTransitionData);
  175. setTransitionRange(_rangeNodes);
  176. setMotionType('hide');
  177. }
  178. } else if (prevData !== data) {
  179. // If whole data changed, we just refresh the list
  180. setPrevData(data);
  181. setTransitionData(data);
  182. }
  183. }, [expandedKeys, data]);
  184. // We should clean up motion if is changed by dragging
  185. React.useEffect(function () {
  186. if (!dragging) {
  187. onMotionEnd();
  188. }
  189. }, [dragging]);
  190. var mergedData = motion ? transitionData : data;
  191. var treeNodeRequiredProps = {
  192. expandedKeys: expandedKeys,
  193. selectedKeys: selectedKeys,
  194. loadedKeys: loadedKeys,
  195. loadingKeys: loadingKeys,
  196. checkedKeys: checkedKeys,
  197. halfCheckedKeys: halfCheckedKeys,
  198. dragOverNodeKey: dragOverNodeKey,
  199. dropPosition: dropPosition,
  200. keyEntities: keyEntities
  201. };
  202. return /*#__PURE__*/React.createElement(React.Fragment, null, focused && activeItem && /*#__PURE__*/React.createElement("span", {
  203. style: HIDDEN_STYLE,
  204. "aria-live": "assertive"
  205. }, getAccessibilityPath(activeItem)), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("input", {
  206. style: HIDDEN_STYLE,
  207. disabled: focusable === false || disabled,
  208. tabIndex: focusable !== false ? tabIndex : null,
  209. onKeyDown: onKeyDown,
  210. onFocus: onFocus,
  211. onBlur: onBlur,
  212. value: "",
  213. onChange: noop,
  214. "aria-label": "for screen reader"
  215. })), /*#__PURE__*/React.createElement("div", {
  216. className: "".concat(prefixCls, "-treenode"),
  217. "aria-hidden": true,
  218. style: {
  219. position: 'absolute',
  220. pointerEvents: 'none',
  221. visibility: 'hidden',
  222. height: 0,
  223. overflow: 'hidden',
  224. border: 0,
  225. padding: 0
  226. }
  227. }, /*#__PURE__*/React.createElement("div", {
  228. className: "".concat(prefixCls, "-indent")
  229. }, /*#__PURE__*/React.createElement("div", {
  230. ref: indentMeasurerRef,
  231. className: "".concat(prefixCls, "-indent-unit")
  232. }))), /*#__PURE__*/React.createElement(VirtualList, _extends({}, domProps, {
  233. data: mergedData,
  234. itemKey: itemKey,
  235. height: height,
  236. fullHeight: false,
  237. virtual: virtual,
  238. itemHeight: itemHeight,
  239. scrollWidth: scrollWidth,
  240. prefixCls: "".concat(prefixCls, "-list"),
  241. ref: listRef,
  242. role: "tree",
  243. onVisibleChange: function onVisibleChange(originList) {
  244. // The best match is using `fullList` - `originList` = `restList`
  245. // and check the `restList` to see if has the MOTION_KEY node
  246. // but this will cause performance issue for long list compare
  247. // we just check `originList` and repeat trigger `onMotionEnd`
  248. if (originList.every(function (item) {
  249. return itemKey(item) !== MOTION_KEY;
  250. })) {
  251. onMotionEnd();
  252. }
  253. }
  254. }), function (treeNode) {
  255. var pos = treeNode.pos,
  256. restProps = Object.assign({}, (_objectDestructuringEmpty(treeNode.data), treeNode.data)),
  257. title = treeNode.title,
  258. key = treeNode.key,
  259. isStart = treeNode.isStart,
  260. isEnd = treeNode.isEnd;
  261. var mergedKey = getKey(key, pos);
  262. delete restProps.key;
  263. delete restProps.children;
  264. var treeNodeProps = getTreeNodeProps(mergedKey, treeNodeRequiredProps);
  265. return /*#__PURE__*/React.createElement(MotionTreeNode, _extends({}, restProps, treeNodeProps, {
  266. title: title,
  267. active: !!activeItem && key === activeItem.key,
  268. pos: pos,
  269. data: treeNode.data,
  270. isStart: isStart,
  271. isEnd: isEnd,
  272. motion: motion,
  273. motionNodes: key === MOTION_KEY ? transitionRange : null,
  274. motionType: motionType,
  275. onMotionStart: onListChangeStart,
  276. onMotionEnd: onMotionEnd,
  277. treeNodeRequiredProps: treeNodeRequiredProps,
  278. onMouseMove: function onMouseMove() {
  279. onActiveChange(null);
  280. }
  281. }));
  282. }));
  283. });
  284. if (process.env.NODE_ENV !== 'production') {
  285. NodeList.displayName = 'NodeList';
  286. }
  287. export default NodeList;