NodeList.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  2. import _extends from "@babel/runtime/helpers/esm/extends";
  3. import { resolveDirective as _resolveDirective, createVNode as _createVNode, Fragment as _Fragment } from "vue";
  4. /**
  5. * Handle virtual list of the TreeNodes.
  6. */
  7. var __rest = this && this.__rest || function (s, e) {
  8. var t = {};
  9. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
  10. if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
  11. if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
  12. }
  13. return t;
  14. };
  15. import { computed, defineComponent, ref, shallowRef, watch } from 'vue';
  16. import VirtualList from '../vc-virtual-list';
  17. import omit from '../_util/omit';
  18. import { useInjectKeysState, useInjectTreeContext } from './contextTypes';
  19. import MotionTreeNode from './MotionTreeNode';
  20. import { nodeListProps } from './props';
  21. import { findExpandedKeys, getExpandRange } from './utils/diffUtil';
  22. import { getKey } from './utils/treeUtil';
  23. const HIDDEN_STYLE = {
  24. width: 0,
  25. height: 0,
  26. display: 'flex',
  27. overflow: 'hidden',
  28. opacity: 0,
  29. border: 0,
  30. padding: 0,
  31. margin: 0
  32. };
  33. const noop = () => {};
  34. export const MOTION_KEY = `RC_TREE_MOTION_${Math.random()}`;
  35. const MotionNode = {
  36. key: MOTION_KEY
  37. };
  38. export const MotionEntity = {
  39. key: MOTION_KEY,
  40. level: 0,
  41. index: 0,
  42. pos: '0',
  43. node: MotionNode,
  44. nodes: [MotionNode]
  45. };
  46. const MotionFlattenData = {
  47. parent: null,
  48. children: [],
  49. pos: MotionEntity.pos,
  50. data: MotionNode,
  51. title: null,
  52. key: MOTION_KEY,
  53. /** Hold empty list here since we do not use it */
  54. isStart: [],
  55. isEnd: []
  56. };
  57. /**
  58. * We only need get visible content items to play the animation.
  59. */
  60. export function getMinimumRangeTransitionRange(list, virtual, height, itemHeight) {
  61. if (virtual === false || !height) {
  62. return list;
  63. }
  64. return list.slice(0, Math.ceil(height / itemHeight) + 1);
  65. }
  66. function itemKey(item) {
  67. const {
  68. key,
  69. pos
  70. } = item;
  71. return getKey(key, pos);
  72. }
  73. function getAccessibilityPath(item) {
  74. let path = String(item.key);
  75. let current = item;
  76. while (current.parent) {
  77. current = current.parent;
  78. path = `${current.key} > ${path}`;
  79. }
  80. return path;
  81. }
  82. export default defineComponent({
  83. compatConfig: {
  84. MODE: 3
  85. },
  86. name: 'NodeList',
  87. inheritAttrs: false,
  88. props: nodeListProps,
  89. setup(props, _ref) {
  90. let {
  91. expose,
  92. attrs
  93. } = _ref;
  94. // =============================== Ref ================================
  95. const listRef = ref();
  96. const indentMeasurerRef = ref();
  97. const {
  98. expandedKeys,
  99. flattenNodes
  100. } = useInjectKeysState();
  101. expose({
  102. scrollTo: scroll => {
  103. listRef.value.scrollTo(scroll);
  104. },
  105. getIndentWidth: () => indentMeasurerRef.value.offsetWidth
  106. });
  107. // ============================== Motion ==============================
  108. const transitionData = shallowRef(flattenNodes.value);
  109. const transitionRange = shallowRef([]);
  110. const motionType = ref(null);
  111. function onMotionEnd() {
  112. transitionData.value = flattenNodes.value;
  113. transitionRange.value = [];
  114. motionType.value = null;
  115. props.onListChangeEnd();
  116. }
  117. const context = useInjectTreeContext();
  118. watch([() => expandedKeys.value.slice(), flattenNodes], (_ref2, _ref3) => {
  119. let [expandedKeys, data] = _ref2;
  120. let [prevExpandedKeys, prevData] = _ref3;
  121. const diffExpanded = findExpandedKeys(prevExpandedKeys, expandedKeys);
  122. if (diffExpanded.key !== null) {
  123. const {
  124. virtual,
  125. height,
  126. itemHeight
  127. } = props;
  128. if (diffExpanded.add) {
  129. const keyIndex = prevData.findIndex(_ref4 => {
  130. let {
  131. key
  132. } = _ref4;
  133. return key === diffExpanded.key;
  134. });
  135. const rangeNodes = getMinimumRangeTransitionRange(getExpandRange(prevData, data, diffExpanded.key), virtual, height, itemHeight);
  136. const newTransitionData = prevData.slice();
  137. newTransitionData.splice(keyIndex + 1, 0, MotionFlattenData);
  138. transitionData.value = newTransitionData;
  139. transitionRange.value = rangeNodes;
  140. motionType.value = 'show';
  141. } else {
  142. const keyIndex = data.findIndex(_ref5 => {
  143. let {
  144. key
  145. } = _ref5;
  146. return key === diffExpanded.key;
  147. });
  148. const rangeNodes = getMinimumRangeTransitionRange(getExpandRange(data, prevData, diffExpanded.key), virtual, height, itemHeight);
  149. const newTransitionData = data.slice();
  150. newTransitionData.splice(keyIndex + 1, 0, MotionFlattenData);
  151. transitionData.value = newTransitionData;
  152. transitionRange.value = rangeNodes;
  153. motionType.value = 'hide';
  154. }
  155. } else if (prevData !== data) {
  156. transitionData.value = data;
  157. }
  158. });
  159. // We should clean up motion if is changed by dragging
  160. watch(() => context.value.dragging, dragging => {
  161. if (!dragging) {
  162. onMotionEnd();
  163. }
  164. });
  165. const mergedData = computed(() => props.motion === undefined ? transitionData.value : flattenNodes.value);
  166. const onActiveChange = () => {
  167. props.onActiveChange(null);
  168. };
  169. return () => {
  170. const _a = _extends(_extends({}, props), attrs),
  171. {
  172. prefixCls,
  173. selectable,
  174. checkable,
  175. disabled,
  176. motion,
  177. height,
  178. itemHeight,
  179. virtual,
  180. focusable,
  181. activeItem,
  182. focused,
  183. tabindex,
  184. onKeydown,
  185. onFocus,
  186. onBlur,
  187. onListChangeStart,
  188. onListChangeEnd
  189. } = _a,
  190. domProps = __rest(_a, ["prefixCls", "selectable", "checkable", "disabled", "motion", "height", "itemHeight", "virtual", "focusable", "activeItem", "focused", "tabindex", "onKeydown", "onFocus", "onBlur", "onListChangeStart", "onListChangeEnd"]);
  191. return _createVNode(_Fragment, null, [focused && activeItem && _createVNode("span", {
  192. "style": HIDDEN_STYLE,
  193. "aria-live": "assertive"
  194. }, [getAccessibilityPath(activeItem)]), _createVNode("div", null, [_createVNode("input", {
  195. "style": HIDDEN_STYLE,
  196. "disabled": focusable === false || disabled,
  197. "tabindex": focusable !== false ? tabindex : null,
  198. "onKeydown": onKeydown,
  199. "onFocus": onFocus,
  200. "onBlur": onBlur,
  201. "value": "",
  202. "onChange": noop,
  203. "aria-label": "for screen reader"
  204. }, null)]), _createVNode("div", {
  205. "class": `${prefixCls}-treenode`,
  206. "aria-hidden": true,
  207. "style": {
  208. position: 'absolute',
  209. pointerEvents: 'none',
  210. visibility: 'hidden',
  211. height: 0,
  212. overflow: 'hidden'
  213. }
  214. }, [_createVNode("div", {
  215. "class": `${prefixCls}-indent`
  216. }, [_createVNode("div", {
  217. "ref": indentMeasurerRef,
  218. "class": `${prefixCls}-indent-unit`
  219. }, null)])]), _createVNode(VirtualList, _objectSpread(_objectSpread({}, omit(domProps, ['onActiveChange'])), {}, {
  220. "data": mergedData.value,
  221. "itemKey": itemKey,
  222. "height": height,
  223. "fullHeight": false,
  224. "virtual": virtual,
  225. "itemHeight": itemHeight,
  226. "prefixCls": `${prefixCls}-list`,
  227. "ref": listRef,
  228. "onVisibleChange": (originList, fullList) => {
  229. const originSet = new Set(originList);
  230. const restList = fullList.filter(item => !originSet.has(item));
  231. // Motion node is not render. Skip motion
  232. if (restList.some(item => itemKey(item) === MOTION_KEY)) {
  233. onMotionEnd();
  234. }
  235. }
  236. }), {
  237. default: treeNode => {
  238. const {
  239. pos
  240. } = treeNode,
  241. restProps = __rest(treeNode.data, []),
  242. {
  243. title,
  244. key,
  245. isStart,
  246. isEnd
  247. } = treeNode;
  248. const mergedKey = getKey(key, pos);
  249. delete restProps.key;
  250. delete restProps.children;
  251. return _createVNode(MotionTreeNode, _objectSpread(_objectSpread({}, restProps), {}, {
  252. "eventKey": mergedKey,
  253. "title": title,
  254. "active": !!activeItem && key === activeItem.key,
  255. "data": treeNode.data,
  256. "isStart": isStart,
  257. "isEnd": isEnd,
  258. "motion": motion,
  259. "motionNodes": key === MOTION_KEY ? transitionRange.value : null,
  260. "motionType": motionType.value,
  261. "onMotionStart": onListChangeStart,
  262. "onMotionEnd": onMotionEnd,
  263. "onMousemove": onActiveChange
  264. }), null);
  265. }
  266. })]);
  267. };
  268. }
  269. });