Tree.js 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116
  1. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  2. import _extends from "@babel/runtime/helpers/esm/extends";
  3. import { createVNode as _createVNode } from "vue";
  4. import { useProvideKeysState, TreeContext } from './contextTypes';
  5. import { getDragChildrenKeys, parseCheckedKeys, conductExpandParent, calcSelectedKeys, calcDropPosition, arrAdd, arrDel, posToArr } from './util';
  6. import { flattenTreeData, convertTreeToData, convertDataToEntities, convertNodePropsToEventData, getTreeNodeProps, fillFieldNames } from './utils/treeUtil';
  7. import NodeList, { MOTION_KEY, MotionEntity } from './NodeList';
  8. import { conductCheck } from './utils/conductUtil';
  9. import DropIndicator from './DropIndicator';
  10. import { computed, defineComponent, onUnmounted, reactive, shallowRef, watch, watchEffect, nextTick, toRaw } from 'vue';
  11. import initDefaultProps from '../_util/props-util/initDefaultProps';
  12. import { treeProps } from './props';
  13. import { warning } from '../vc-util/warning';
  14. import KeyCode from '../_util/KeyCode';
  15. import classNames from '../_util/classNames';
  16. import pickAttrs from '../_util/pickAttrs';
  17. import useMaxLevel from './useMaxLevel';
  18. const MAX_RETRY_TIMES = 10;
  19. export default defineComponent({
  20. compatConfig: {
  21. MODE: 3
  22. },
  23. name: 'Tree',
  24. inheritAttrs: false,
  25. props: initDefaultProps(treeProps(), {
  26. prefixCls: 'vc-tree',
  27. showLine: false,
  28. showIcon: true,
  29. selectable: true,
  30. multiple: false,
  31. checkable: false,
  32. disabled: false,
  33. checkStrictly: false,
  34. draggable: false,
  35. expandAction: false,
  36. defaultExpandParent: true,
  37. autoExpandParent: false,
  38. defaultExpandAll: false,
  39. defaultExpandedKeys: [],
  40. defaultCheckedKeys: [],
  41. defaultSelectedKeys: [],
  42. dropIndicatorRender: DropIndicator,
  43. allowDrop: () => true
  44. }),
  45. setup(props, _ref) {
  46. let {
  47. attrs,
  48. slots,
  49. expose
  50. } = _ref;
  51. const destroyed = shallowRef(false);
  52. let delayedDragEnterLogic = {};
  53. const indent = shallowRef();
  54. const selectedKeys = shallowRef([]);
  55. const checkedKeys = shallowRef([]);
  56. const halfCheckedKeys = shallowRef([]);
  57. const loadedKeys = shallowRef([]);
  58. const loadingKeys = shallowRef([]);
  59. const expandedKeys = shallowRef([]);
  60. const loadingRetryTimes = {};
  61. const dragState = reactive({
  62. draggingNodeKey: null,
  63. dragChildrenKeys: [],
  64. // dropTargetKey is the key of abstract-drop-node
  65. // the abstract-drop-node is the real drop node when drag and drop
  66. // not the DOM drag over node
  67. dropTargetKey: null,
  68. dropPosition: null,
  69. dropContainerKey: null,
  70. dropLevelOffset: null,
  71. dropTargetPos: null,
  72. dropAllowed: true,
  73. // the abstract-drag-over-node
  74. // if mouse is on the bottom of top dom node or no the top of the bottom dom node
  75. // abstract-drag-over-node is the top node
  76. dragOverNodeKey: null
  77. });
  78. const treeData = shallowRef([]);
  79. watch([() => props.treeData, () => props.children], () => {
  80. treeData.value = props.treeData !== undefined ? props.treeData.slice() : convertTreeToData(toRaw(props.children));
  81. }, {
  82. immediate: true,
  83. deep: true
  84. });
  85. const keyEntities = shallowRef({});
  86. const focused = shallowRef(false);
  87. const activeKey = shallowRef(null);
  88. const listChanging = shallowRef(false);
  89. const fieldNames = computed(() => fillFieldNames(props.fieldNames));
  90. const listRef = shallowRef();
  91. let dragStartMousePosition = null;
  92. let dragNode = null;
  93. let currentMouseOverDroppableNodeKey = null;
  94. const treeNodeRequiredProps = computed(() => {
  95. return {
  96. expandedKeysSet: expandedKeysSet.value,
  97. selectedKeysSet: selectedKeysSet.value,
  98. loadedKeysSet: loadedKeysSet.value,
  99. loadingKeysSet: loadingKeysSet.value,
  100. checkedKeysSet: checkedKeysSet.value,
  101. halfCheckedKeysSet: halfCheckedKeysSet.value,
  102. dragOverNodeKey: dragState.dragOverNodeKey,
  103. dropPosition: dragState.dropPosition,
  104. keyEntities: keyEntities.value
  105. };
  106. });
  107. const expandedKeysSet = computed(() => {
  108. return new Set(expandedKeys.value);
  109. });
  110. const selectedKeysSet = computed(() => {
  111. return new Set(selectedKeys.value);
  112. });
  113. const loadedKeysSet = computed(() => {
  114. return new Set(loadedKeys.value);
  115. });
  116. const loadingKeysSet = computed(() => {
  117. return new Set(loadingKeys.value);
  118. });
  119. const checkedKeysSet = computed(() => {
  120. return new Set(checkedKeys.value);
  121. });
  122. const halfCheckedKeysSet = computed(() => {
  123. return new Set(halfCheckedKeys.value);
  124. });
  125. watchEffect(() => {
  126. if (treeData.value) {
  127. const entitiesMap = convertDataToEntities(treeData.value, {
  128. fieldNames: fieldNames.value
  129. });
  130. keyEntities.value = _extends({
  131. [MOTION_KEY]: MotionEntity
  132. }, entitiesMap.keyEntities);
  133. }
  134. });
  135. let init = false; // 处理 defaultXxxx api, 仅仅首次有效
  136. watch([() => props.expandedKeys, () => props.autoExpandParent, keyEntities],
  137. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  138. (_ref2, _ref3) => {
  139. let [_newKeys, newAutoExpandParent] = _ref2;
  140. let [_oldKeys, oldAutoExpandParent] = _ref3;
  141. let keys = expandedKeys.value;
  142. // ================ expandedKeys =================
  143. if (props.expandedKeys !== undefined || init && newAutoExpandParent !== oldAutoExpandParent) {
  144. keys = props.autoExpandParent || !init && props.defaultExpandParent ? conductExpandParent(props.expandedKeys, keyEntities.value) : props.expandedKeys;
  145. } else if (!init && props.defaultExpandAll) {
  146. const cloneKeyEntities = _extends({}, keyEntities.value);
  147. delete cloneKeyEntities[MOTION_KEY];
  148. keys = Object.keys(cloneKeyEntities).map(key => cloneKeyEntities[key].key);
  149. } else if (!init && props.defaultExpandedKeys) {
  150. keys = props.autoExpandParent || props.defaultExpandParent ? conductExpandParent(props.defaultExpandedKeys, keyEntities.value) : props.defaultExpandedKeys;
  151. }
  152. if (keys) {
  153. expandedKeys.value = keys;
  154. }
  155. init = true;
  156. }, {
  157. immediate: true
  158. });
  159. // ================ flattenNodes =================
  160. const flattenNodes = shallowRef([]);
  161. watchEffect(() => {
  162. flattenNodes.value = flattenTreeData(treeData.value, expandedKeys.value, fieldNames.value);
  163. });
  164. // ================ selectedKeys =================
  165. watchEffect(() => {
  166. if (props.selectable) {
  167. if (props.selectedKeys !== undefined) {
  168. selectedKeys.value = calcSelectedKeys(props.selectedKeys, props);
  169. } else if (!init && props.defaultSelectedKeys) {
  170. selectedKeys.value = calcSelectedKeys(props.defaultSelectedKeys, props);
  171. }
  172. }
  173. });
  174. const {
  175. maxLevel,
  176. levelEntities
  177. } = useMaxLevel(keyEntities);
  178. // ================= checkedKeys =================
  179. watchEffect(() => {
  180. if (props.checkable) {
  181. let checkedKeyEntity;
  182. if (props.checkedKeys !== undefined) {
  183. checkedKeyEntity = parseCheckedKeys(props.checkedKeys) || {};
  184. } else if (!init && props.defaultCheckedKeys) {
  185. checkedKeyEntity = parseCheckedKeys(props.defaultCheckedKeys) || {};
  186. } else if (treeData.value) {
  187. // If `treeData` changed, we also need check it
  188. checkedKeyEntity = parseCheckedKeys(props.checkedKeys) || {
  189. checkedKeys: checkedKeys.value,
  190. halfCheckedKeys: halfCheckedKeys.value
  191. };
  192. }
  193. if (checkedKeyEntity) {
  194. let {
  195. checkedKeys: newCheckedKeys = [],
  196. halfCheckedKeys: newHalfCheckedKeys = []
  197. } = checkedKeyEntity;
  198. if (!props.checkStrictly) {
  199. const conductKeys = conductCheck(newCheckedKeys, true, keyEntities.value, maxLevel.value, levelEntities.value);
  200. ({
  201. checkedKeys: newCheckedKeys,
  202. halfCheckedKeys: newHalfCheckedKeys
  203. } = conductKeys);
  204. }
  205. checkedKeys.value = newCheckedKeys;
  206. halfCheckedKeys.value = newHalfCheckedKeys;
  207. }
  208. }
  209. });
  210. // ================= loadedKeys ==================
  211. watchEffect(() => {
  212. if (props.loadedKeys) {
  213. loadedKeys.value = props.loadedKeys;
  214. }
  215. });
  216. const resetDragState = () => {
  217. _extends(dragState, {
  218. dragOverNodeKey: null,
  219. dropPosition: null,
  220. dropLevelOffset: null,
  221. dropTargetKey: null,
  222. dropContainerKey: null,
  223. dropTargetPos: null,
  224. dropAllowed: false
  225. });
  226. };
  227. const scrollTo = scroll => {
  228. listRef.value.scrollTo(scroll);
  229. };
  230. watch(() => props.activeKey, () => {
  231. if (props.activeKey !== undefined) {
  232. activeKey.value = props.activeKey;
  233. }
  234. }, {
  235. immediate: true
  236. });
  237. watch(activeKey, val => {
  238. nextTick(() => {
  239. if (val !== null) {
  240. scrollTo({
  241. key: val
  242. });
  243. }
  244. });
  245. }, {
  246. immediate: true,
  247. flush: 'post'
  248. });
  249. // =========================== Expanded ===========================
  250. /** Set uncontrolled `expandedKeys`. This will also auto update `flattenNodes`. */
  251. const setExpandedKeys = keys => {
  252. if (props.expandedKeys === undefined) {
  253. expandedKeys.value = keys;
  254. }
  255. };
  256. const cleanDragState = () => {
  257. if (dragState.draggingNodeKey !== null) {
  258. _extends(dragState, {
  259. draggingNodeKey: null,
  260. dropPosition: null,
  261. dropContainerKey: null,
  262. dropTargetKey: null,
  263. dropLevelOffset: null,
  264. dropAllowed: true,
  265. dragOverNodeKey: null
  266. });
  267. }
  268. dragStartMousePosition = null;
  269. currentMouseOverDroppableNodeKey = null;
  270. };
  271. // if onNodeDragEnd is called, onWindowDragEnd won't be called since stopPropagation() is called
  272. const onNodeDragEnd = (event, node) => {
  273. const {
  274. onDragend
  275. } = props;
  276. dragState.dragOverNodeKey = null;
  277. cleanDragState();
  278. onDragend === null || onDragend === void 0 ? void 0 : onDragend({
  279. event,
  280. node: node.eventData
  281. });
  282. dragNode = null;
  283. };
  284. // since stopPropagation() is called in treeNode
  285. // if onWindowDrag is called, whice means state is keeped, drag state should be cleared
  286. const onWindowDragEnd = event => {
  287. onNodeDragEnd(event, null, true);
  288. window.removeEventListener('dragend', onWindowDragEnd);
  289. };
  290. const onNodeDragStart = (event, node) => {
  291. const {
  292. onDragstart
  293. } = props;
  294. const {
  295. eventKey,
  296. eventData
  297. } = node;
  298. dragNode = node;
  299. dragStartMousePosition = {
  300. x: event.clientX,
  301. y: event.clientY
  302. };
  303. const newExpandedKeys = arrDel(expandedKeys.value, eventKey);
  304. dragState.draggingNodeKey = eventKey;
  305. dragState.dragChildrenKeys = getDragChildrenKeys(eventKey, keyEntities.value);
  306. indent.value = listRef.value.getIndentWidth();
  307. setExpandedKeys(newExpandedKeys);
  308. window.addEventListener('dragend', onWindowDragEnd);
  309. if (onDragstart) {
  310. onDragstart({
  311. event,
  312. node: eventData
  313. });
  314. }
  315. };
  316. /**
  317. * [Legacy] Select handler is smaller than node,
  318. * so that this will trigger when drag enter node or select handler.
  319. * This is a little tricky if customize css without padding.
  320. * Better for use mouse move event to refresh drag state.
  321. * But let's just keep it to avoid event trigger logic change.
  322. */
  323. const onNodeDragEnter = (event, node) => {
  324. const {
  325. onDragenter,
  326. onExpand,
  327. allowDrop,
  328. direction
  329. } = props;
  330. const {
  331. pos,
  332. eventKey
  333. } = node;
  334. // record the key of node which is latest entered, used in dragleave event.
  335. if (currentMouseOverDroppableNodeKey !== eventKey) {
  336. currentMouseOverDroppableNodeKey = eventKey;
  337. }
  338. if (!dragNode) {
  339. resetDragState();
  340. return;
  341. }
  342. const {
  343. dropPosition,
  344. dropLevelOffset,
  345. dropTargetKey,
  346. dropContainerKey,
  347. dropTargetPos,
  348. dropAllowed,
  349. dragOverNodeKey
  350. } = calcDropPosition(event, dragNode, node, indent.value, dragStartMousePosition, allowDrop, flattenNodes.value, keyEntities.value, expandedKeysSet.value, direction);
  351. if (
  352. // don't allow drop inside its children
  353. dragState.dragChildrenKeys.indexOf(dropTargetKey) !== -1 ||
  354. // don't allow drop when drop is not allowed caculated by calcDropPosition
  355. !dropAllowed) {
  356. resetDragState();
  357. return;
  358. }
  359. // Side effect for delay drag
  360. if (!delayedDragEnterLogic) {
  361. delayedDragEnterLogic = {};
  362. }
  363. Object.keys(delayedDragEnterLogic).forEach(key => {
  364. clearTimeout(delayedDragEnterLogic[key]);
  365. });
  366. if (dragNode.eventKey !== node.eventKey) {
  367. // hoist expand logic here
  368. // since if logic is on the bottom
  369. // it will be blocked by abstract dragover node check
  370. // => if you dragenter from top, you mouse will still be consider as in the top node
  371. delayedDragEnterLogic[pos] = window.setTimeout(() => {
  372. if (dragState.draggingNodeKey === null) return;
  373. let newExpandedKeys = expandedKeys.value.slice();
  374. const entity = keyEntities.value[node.eventKey];
  375. if (entity && (entity.children || []).length) {
  376. newExpandedKeys = arrAdd(expandedKeys.value, node.eventKey);
  377. }
  378. setExpandedKeys(newExpandedKeys);
  379. if (onExpand) {
  380. onExpand(newExpandedKeys, {
  381. node: node.eventData,
  382. expanded: true,
  383. nativeEvent: event
  384. });
  385. }
  386. }, 800);
  387. }
  388. // Skip if drag node is self
  389. if (dragNode.eventKey === dropTargetKey && dropLevelOffset === 0) {
  390. resetDragState();
  391. return;
  392. }
  393. // Update drag over node and drag state
  394. _extends(dragState, {
  395. dragOverNodeKey,
  396. dropPosition,
  397. dropLevelOffset,
  398. dropTargetKey,
  399. dropContainerKey,
  400. dropTargetPos,
  401. dropAllowed
  402. });
  403. if (onDragenter) {
  404. onDragenter({
  405. event,
  406. node: node.eventData,
  407. expandedKeys: expandedKeys.value
  408. });
  409. }
  410. };
  411. const onNodeDragOver = (event, node) => {
  412. const {
  413. onDragover,
  414. allowDrop,
  415. direction
  416. } = props;
  417. if (!dragNode) {
  418. return;
  419. }
  420. const {
  421. dropPosition,
  422. dropLevelOffset,
  423. dropTargetKey,
  424. dropContainerKey,
  425. dropAllowed,
  426. dropTargetPos,
  427. dragOverNodeKey
  428. } = calcDropPosition(event, dragNode, node, indent.value, dragStartMousePosition, allowDrop, flattenNodes.value, keyEntities.value, expandedKeysSet.value, direction);
  429. if (dragState.dragChildrenKeys.indexOf(dropTargetKey) !== -1 || !dropAllowed) {
  430. // don't allow drop inside its children
  431. // don't allow drop when drop is not allowed caculated by calcDropPosition
  432. return;
  433. }
  434. // Update drag position
  435. if (dragNode.eventKey === dropTargetKey && dropLevelOffset === 0) {
  436. if (!(dragState.dropPosition === null && dragState.dropLevelOffset === null && dragState.dropTargetKey === null && dragState.dropContainerKey === null && dragState.dropTargetPos === null && dragState.dropAllowed === false && dragState.dragOverNodeKey === null)) {
  437. resetDragState();
  438. }
  439. } else if (!(dropPosition === dragState.dropPosition && dropLevelOffset === dragState.dropLevelOffset && dropTargetKey === dragState.dropTargetKey && dropContainerKey === dragState.dropContainerKey && dropTargetPos === dragState.dropTargetPos && dropAllowed === dragState.dropAllowed && dragOverNodeKey === dragState.dragOverNodeKey)) {
  440. _extends(dragState, {
  441. dropPosition,
  442. dropLevelOffset,
  443. dropTargetKey,
  444. dropContainerKey,
  445. dropTargetPos,
  446. dropAllowed,
  447. dragOverNodeKey
  448. });
  449. }
  450. if (onDragover) {
  451. onDragover({
  452. event,
  453. node: node.eventData
  454. });
  455. }
  456. };
  457. const onNodeDragLeave = (event, node) => {
  458. // if it is outside the droppable area
  459. // currentMouseOverDroppableNodeKey will be updated in dragenter event when into another droppable receiver.
  460. if (currentMouseOverDroppableNodeKey === node.eventKey && !event.currentTarget.contains(event.relatedTarget)) {
  461. resetDragState();
  462. currentMouseOverDroppableNodeKey = null;
  463. }
  464. const {
  465. onDragleave
  466. } = props;
  467. if (onDragleave) {
  468. onDragleave({
  469. event,
  470. node: node.eventData
  471. });
  472. }
  473. };
  474. const onNodeDrop = function (event, _node) {
  475. let outsideTree = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
  476. var _a;
  477. const {
  478. dragChildrenKeys,
  479. dropPosition,
  480. dropTargetKey,
  481. dropTargetPos,
  482. dropAllowed
  483. } = dragState;
  484. if (!dropAllowed) return;
  485. const {
  486. onDrop
  487. } = props;
  488. dragState.dragOverNodeKey = null;
  489. cleanDragState();
  490. if (dropTargetKey === null) return;
  491. const abstractDropNodeProps = _extends(_extends({}, getTreeNodeProps(dropTargetKey, toRaw(treeNodeRequiredProps.value))), {
  492. active: ((_a = activeItem.value) === null || _a === void 0 ? void 0 : _a.key) === dropTargetKey,
  493. data: keyEntities.value[dropTargetKey].node
  494. });
  495. const dropToChild = dragChildrenKeys.indexOf(dropTargetKey) !== -1;
  496. warning(!dropToChild, "Can not drop to dragNode's children node. Maybe this is a bug of ant-design-vue. Please report an issue.");
  497. const posArr = posToArr(dropTargetPos);
  498. const dropResult = {
  499. event,
  500. node: convertNodePropsToEventData(abstractDropNodeProps),
  501. dragNode: dragNode ? dragNode.eventData : null,
  502. dragNodesKeys: [dragNode.eventKey].concat(dragChildrenKeys),
  503. dropToGap: dropPosition !== 0,
  504. dropPosition: dropPosition + Number(posArr[posArr.length - 1])
  505. };
  506. if (!outsideTree) {
  507. onDrop === null || onDrop === void 0 ? void 0 : onDrop(dropResult);
  508. }
  509. dragNode = null;
  510. };
  511. const triggerExpandActionExpand = (e, treeNode) => {
  512. const {
  513. expanded,
  514. key
  515. } = treeNode;
  516. const node = flattenNodes.value.filter(nodeItem => nodeItem.key === key)[0];
  517. const eventNode = convertNodePropsToEventData(_extends(_extends({}, getTreeNodeProps(key, treeNodeRequiredProps.value)), {
  518. data: node.data
  519. }));
  520. setExpandedKeys(expanded ? arrDel(expandedKeys.value, key) : arrAdd(expandedKeys.value, key));
  521. onNodeExpand(e, eventNode);
  522. };
  523. const onNodeClick = (e, treeNode) => {
  524. const {
  525. onClick,
  526. expandAction
  527. } = props;
  528. if (expandAction === 'click') {
  529. triggerExpandActionExpand(e, treeNode);
  530. }
  531. if (onClick) {
  532. onClick(e, treeNode);
  533. }
  534. };
  535. const onNodeDoubleClick = (e, treeNode) => {
  536. const {
  537. onDblclick,
  538. expandAction
  539. } = props;
  540. if (expandAction === 'doubleclick' || expandAction === 'dblclick') {
  541. triggerExpandActionExpand(e, treeNode);
  542. }
  543. if (onDblclick) {
  544. onDblclick(e, treeNode);
  545. }
  546. };
  547. const onNodeSelect = (e, treeNode) => {
  548. let newSelectedKeys = selectedKeys.value;
  549. const {
  550. onSelect,
  551. multiple
  552. } = props;
  553. const {
  554. selected
  555. } = treeNode;
  556. const key = treeNode[fieldNames.value.key];
  557. const targetSelected = !selected;
  558. // Update selected keys
  559. if (!targetSelected) {
  560. newSelectedKeys = arrDel(newSelectedKeys, key);
  561. } else if (!multiple) {
  562. newSelectedKeys = [key];
  563. } else {
  564. newSelectedKeys = arrAdd(newSelectedKeys, key);
  565. }
  566. // [Legacy] Not found related usage in doc or upper libs
  567. const keyEntitiesValue = keyEntities.value;
  568. const selectedNodes = newSelectedKeys.map(selectedKey => {
  569. const entity = keyEntitiesValue[selectedKey];
  570. if (!entity) return null;
  571. return entity.node;
  572. }).filter(node => node);
  573. if (props.selectedKeys === undefined) {
  574. selectedKeys.value = newSelectedKeys;
  575. }
  576. if (onSelect) {
  577. onSelect(newSelectedKeys, {
  578. event: 'select',
  579. selected: targetSelected,
  580. node: treeNode,
  581. selectedNodes,
  582. nativeEvent: e
  583. });
  584. }
  585. };
  586. const onNodeCheck = (e, treeNode, checked) => {
  587. const {
  588. checkStrictly,
  589. onCheck
  590. } = props;
  591. const key = treeNode[fieldNames.value.key];
  592. // Prepare trigger arguments
  593. let checkedObj;
  594. const eventObj = {
  595. event: 'check',
  596. node: treeNode,
  597. checked,
  598. nativeEvent: e
  599. };
  600. const keyEntitiesValue = keyEntities.value;
  601. if (checkStrictly) {
  602. const newCheckedKeys = checked ? arrAdd(checkedKeys.value, key) : arrDel(checkedKeys.value, key);
  603. const newHalfCheckedKeys = arrDel(halfCheckedKeys.value, key);
  604. checkedObj = {
  605. checked: newCheckedKeys,
  606. halfChecked: newHalfCheckedKeys
  607. };
  608. eventObj.checkedNodes = newCheckedKeys.map(checkedKey => keyEntitiesValue[checkedKey]).filter(entity => entity).map(entity => entity.node);
  609. if (props.checkedKeys === undefined) {
  610. checkedKeys.value = newCheckedKeys;
  611. }
  612. } else {
  613. // Always fill first
  614. let {
  615. checkedKeys: newCheckedKeys,
  616. halfCheckedKeys: newHalfCheckedKeys
  617. } = conductCheck([...checkedKeys.value, key], true, keyEntitiesValue, maxLevel.value, levelEntities.value);
  618. // If remove, we do it again to correction
  619. if (!checked) {
  620. const keySet = new Set(newCheckedKeys);
  621. keySet.delete(key);
  622. ({
  623. checkedKeys: newCheckedKeys,
  624. halfCheckedKeys: newHalfCheckedKeys
  625. } = conductCheck(Array.from(keySet), {
  626. checked: false,
  627. halfCheckedKeys: newHalfCheckedKeys
  628. }, keyEntitiesValue, maxLevel.value, levelEntities.value));
  629. }
  630. checkedObj = newCheckedKeys;
  631. // [Legacy] This is used for vc-tree-select`
  632. eventObj.checkedNodes = [];
  633. eventObj.checkedNodesPositions = [];
  634. eventObj.halfCheckedKeys = newHalfCheckedKeys;
  635. newCheckedKeys.forEach(checkedKey => {
  636. const entity = keyEntitiesValue[checkedKey];
  637. if (!entity) return;
  638. const {
  639. node,
  640. pos
  641. } = entity;
  642. eventObj.checkedNodes.push(node);
  643. eventObj.checkedNodesPositions.push({
  644. node,
  645. pos
  646. });
  647. });
  648. if (props.checkedKeys === undefined) {
  649. checkedKeys.value = newCheckedKeys;
  650. halfCheckedKeys.value = newHalfCheckedKeys;
  651. }
  652. }
  653. if (onCheck) {
  654. onCheck(checkedObj, eventObj);
  655. }
  656. };
  657. const onNodeLoad = treeNode => {
  658. const key = treeNode[fieldNames.value.key];
  659. const loadPromise = new Promise((resolve, reject) => {
  660. // We need to get the latest state of loading/loaded keys
  661. const {
  662. loadData,
  663. onLoad
  664. } = props;
  665. if (!loadData || loadedKeysSet.value.has(key) || loadingKeysSet.value.has(key)) {
  666. return null;
  667. }
  668. // Process load data
  669. const promise = loadData(treeNode);
  670. promise.then(() => {
  671. const newLoadedKeys = arrAdd(loadedKeys.value, key);
  672. const newLoadingKeys = arrDel(loadingKeys.value, key);
  673. // onLoad should trigger before internal setState to avoid `loadData` trigger twice.
  674. // https://github.com/ant-design/ant-design/issues/12464
  675. if (onLoad) {
  676. onLoad(newLoadedKeys, {
  677. event: 'load',
  678. node: treeNode
  679. });
  680. }
  681. if (props.loadedKeys === undefined) {
  682. loadedKeys.value = newLoadedKeys;
  683. }
  684. loadingKeys.value = newLoadingKeys;
  685. resolve();
  686. }).catch(e => {
  687. const newLoadingKeys = arrDel(loadingKeys.value, key);
  688. loadingKeys.value = newLoadingKeys;
  689. // If exceed max retry times, we give up retry
  690. loadingRetryTimes[key] = (loadingRetryTimes[key] || 0) + 1;
  691. if (loadingRetryTimes[key] >= MAX_RETRY_TIMES) {
  692. warning(false, 'Retry for `loadData` many times but still failed. No more retry.');
  693. const newLoadedKeys = arrAdd(loadedKeys.value, key);
  694. if (props.loadedKeys === undefined) {
  695. loadedKeys.value = newLoadedKeys;
  696. }
  697. resolve();
  698. }
  699. reject(e);
  700. });
  701. loadingKeys.value = arrAdd(loadingKeys.value, key);
  702. });
  703. // Not care warning if we ignore this
  704. loadPromise.catch(() => {});
  705. return loadPromise;
  706. };
  707. const onNodeMouseEnter = (event, node) => {
  708. const {
  709. onMouseenter
  710. } = props;
  711. if (onMouseenter) {
  712. onMouseenter({
  713. event,
  714. node
  715. });
  716. }
  717. };
  718. const onNodeMouseLeave = (event, node) => {
  719. const {
  720. onMouseleave
  721. } = props;
  722. if (onMouseleave) {
  723. onMouseleave({
  724. event,
  725. node
  726. });
  727. }
  728. };
  729. const onNodeContextMenu = (event, node) => {
  730. const {
  731. onRightClick
  732. } = props;
  733. if (onRightClick) {
  734. event.preventDefault();
  735. onRightClick({
  736. event,
  737. node
  738. });
  739. }
  740. };
  741. const onFocus = e => {
  742. const {
  743. onFocus
  744. } = props;
  745. focused.value = true;
  746. if (onFocus) {
  747. onFocus(e);
  748. }
  749. };
  750. const onBlur = e => {
  751. const {
  752. onBlur
  753. } = props;
  754. focused.value = false;
  755. onActiveChange(null);
  756. if (onBlur) {
  757. onBlur(e);
  758. }
  759. };
  760. const onNodeExpand = (e, treeNode) => {
  761. let newExpandedKeys = expandedKeys.value;
  762. const {
  763. onExpand,
  764. loadData
  765. } = props;
  766. const {
  767. expanded
  768. } = treeNode;
  769. const key = treeNode[fieldNames.value.key];
  770. // Do nothing when motion is in progress
  771. if (listChanging.value) {
  772. return;
  773. }
  774. // Update selected keys
  775. const index = newExpandedKeys.indexOf(key);
  776. const targetExpanded = !expanded;
  777. warning(expanded && index !== -1 || !expanded && index === -1, 'Expand state not sync with index check');
  778. if (targetExpanded) {
  779. newExpandedKeys = arrAdd(newExpandedKeys, key);
  780. } else {
  781. newExpandedKeys = arrDel(newExpandedKeys, key);
  782. }
  783. setExpandedKeys(newExpandedKeys);
  784. if (onExpand) {
  785. onExpand(newExpandedKeys, {
  786. node: treeNode,
  787. expanded: targetExpanded,
  788. nativeEvent: e
  789. });
  790. }
  791. // Async Load data
  792. if (targetExpanded && loadData) {
  793. const loadPromise = onNodeLoad(treeNode);
  794. if (loadPromise) {
  795. loadPromise.then(() => {
  796. // [Legacy] Refresh logic
  797. // const newFlattenTreeData = flattenTreeData(
  798. // treeData.value,
  799. // newExpandedKeys,
  800. // fieldNames.value,
  801. // );
  802. // flattenNodes.value = newFlattenTreeData;
  803. }).catch(e => {
  804. const expandedKeysToRestore = arrDel(expandedKeys.value, key);
  805. setExpandedKeys(expandedKeysToRestore);
  806. Promise.reject(e);
  807. });
  808. }
  809. }
  810. };
  811. const onListChangeStart = () => {
  812. listChanging.value = true;
  813. };
  814. const onListChangeEnd = () => {
  815. setTimeout(() => {
  816. listChanging.value = false;
  817. });
  818. };
  819. // =========================== Keyboard ===========================
  820. const onActiveChange = newActiveKey => {
  821. const {
  822. onActiveChange
  823. } = props;
  824. if (activeKey.value === newActiveKey) {
  825. return;
  826. }
  827. if (props.activeKey !== undefined) {
  828. activeKey.value = newActiveKey;
  829. }
  830. if (newActiveKey !== null) {
  831. scrollTo({
  832. key: newActiveKey
  833. });
  834. }
  835. if (onActiveChange) {
  836. onActiveChange(newActiveKey);
  837. }
  838. };
  839. const activeItem = computed(() => {
  840. if (activeKey.value === null) {
  841. return null;
  842. }
  843. return flattenNodes.value.find(_ref4 => {
  844. let {
  845. key
  846. } = _ref4;
  847. return key === activeKey.value;
  848. }) || null;
  849. });
  850. const offsetActiveKey = offset => {
  851. let index = flattenNodes.value.findIndex(_ref5 => {
  852. let {
  853. key
  854. } = _ref5;
  855. return key === activeKey.value;
  856. });
  857. // Align with index
  858. if (index === -1 && offset < 0) {
  859. index = flattenNodes.value.length;
  860. }
  861. index = (index + offset + flattenNodes.value.length) % flattenNodes.value.length;
  862. const item = flattenNodes.value[index];
  863. if (item) {
  864. const {
  865. key
  866. } = item;
  867. onActiveChange(key);
  868. } else {
  869. onActiveChange(null);
  870. }
  871. };
  872. const activeItemEventNode = computed(() => {
  873. return convertNodePropsToEventData(_extends(_extends({}, getTreeNodeProps(activeKey.value, treeNodeRequiredProps.value)), {
  874. data: activeItem.value.data,
  875. active: true
  876. }));
  877. });
  878. const onKeydown = event => {
  879. const {
  880. onKeydown,
  881. checkable,
  882. selectable
  883. } = props;
  884. // >>>>>>>>>> Direction
  885. switch (event.which) {
  886. case KeyCode.UP:
  887. {
  888. offsetActiveKey(-1);
  889. event.preventDefault();
  890. break;
  891. }
  892. case KeyCode.DOWN:
  893. {
  894. offsetActiveKey(1);
  895. event.preventDefault();
  896. break;
  897. }
  898. }
  899. // >>>>>>>>>> Expand & Selection
  900. const item = activeItem.value;
  901. if (item && item.data) {
  902. const expandable = item.data.isLeaf === false || !!(item.data.children || []).length;
  903. const eventNode = activeItemEventNode.value;
  904. switch (event.which) {
  905. // >>> Expand
  906. case KeyCode.LEFT:
  907. {
  908. // Collapse if possible
  909. if (expandable && expandedKeysSet.value.has(activeKey.value)) {
  910. onNodeExpand({}, eventNode);
  911. } else if (item.parent) {
  912. onActiveChange(item.parent.key);
  913. }
  914. event.preventDefault();
  915. break;
  916. }
  917. case KeyCode.RIGHT:
  918. {
  919. // Expand if possible
  920. if (expandable && !expandedKeysSet.value.has(activeKey.value)) {
  921. onNodeExpand({}, eventNode);
  922. } else if (item.children && item.children.length) {
  923. onActiveChange(item.children[0].key);
  924. }
  925. event.preventDefault();
  926. break;
  927. }
  928. // Selection
  929. case KeyCode.ENTER:
  930. case KeyCode.SPACE:
  931. {
  932. if (checkable && !eventNode.disabled && eventNode.checkable !== false && !eventNode.disableCheckbox) {
  933. onNodeCheck({}, eventNode, !checkedKeysSet.value.has(activeKey.value));
  934. } else if (!checkable && selectable && !eventNode.disabled && eventNode.selectable !== false) {
  935. onNodeSelect({}, eventNode);
  936. }
  937. break;
  938. }
  939. }
  940. }
  941. if (onKeydown) {
  942. onKeydown(event);
  943. }
  944. };
  945. expose({
  946. onNodeExpand,
  947. scrollTo,
  948. onKeydown,
  949. selectedKeys: computed(() => selectedKeys.value),
  950. checkedKeys: computed(() => checkedKeys.value),
  951. halfCheckedKeys: computed(() => halfCheckedKeys.value),
  952. loadedKeys: computed(() => loadedKeys.value),
  953. loadingKeys: computed(() => loadingKeys.value),
  954. expandedKeys: computed(() => expandedKeys.value)
  955. });
  956. onUnmounted(() => {
  957. window.removeEventListener('dragend', onWindowDragEnd);
  958. destroyed.value = true;
  959. });
  960. useProvideKeysState({
  961. expandedKeys,
  962. selectedKeys,
  963. loadedKeys,
  964. loadingKeys,
  965. checkedKeys,
  966. halfCheckedKeys,
  967. expandedKeysSet,
  968. selectedKeysSet,
  969. loadedKeysSet,
  970. loadingKeysSet,
  971. checkedKeysSet,
  972. halfCheckedKeysSet,
  973. flattenNodes
  974. });
  975. return () => {
  976. const {
  977. // focused,
  978. // flattenNodes,
  979. // keyEntities,
  980. draggingNodeKey,
  981. // activeKey,
  982. dropLevelOffset,
  983. dropContainerKey,
  984. dropTargetKey,
  985. dropPosition,
  986. dragOverNodeKey
  987. // indent,
  988. } = dragState;
  989. const {
  990. prefixCls,
  991. showLine,
  992. focusable,
  993. tabindex = 0,
  994. selectable,
  995. showIcon,
  996. icon = slots.icon,
  997. switcherIcon,
  998. draggable,
  999. checkable,
  1000. checkStrictly,
  1001. disabled,
  1002. motion,
  1003. loadData,
  1004. filterTreeNode,
  1005. height,
  1006. itemHeight,
  1007. virtual,
  1008. dropIndicatorRender,
  1009. onContextmenu,
  1010. onScroll,
  1011. direction,
  1012. rootClassName,
  1013. rootStyle
  1014. } = props;
  1015. const {
  1016. class: className,
  1017. style
  1018. } = attrs;
  1019. const domProps = pickAttrs(_extends(_extends({}, props), attrs), {
  1020. aria: true,
  1021. data: true
  1022. });
  1023. // It's better move to hooks but we just simply keep here
  1024. let draggableConfig;
  1025. if (draggable) {
  1026. if (typeof draggable === 'object') {
  1027. draggableConfig = draggable;
  1028. } else if (typeof draggable === 'function') {
  1029. draggableConfig = {
  1030. nodeDraggable: draggable
  1031. };
  1032. } else {
  1033. draggableConfig = {};
  1034. }
  1035. } else {
  1036. draggableConfig = false;
  1037. }
  1038. return _createVNode(TreeContext, {
  1039. "value": {
  1040. prefixCls,
  1041. selectable,
  1042. showIcon,
  1043. icon,
  1044. switcherIcon,
  1045. draggable: draggableConfig,
  1046. draggingNodeKey,
  1047. checkable,
  1048. customCheckable: slots.checkable,
  1049. checkStrictly,
  1050. disabled,
  1051. keyEntities: keyEntities.value,
  1052. dropLevelOffset,
  1053. dropContainerKey,
  1054. dropTargetKey,
  1055. dropPosition,
  1056. dragOverNodeKey,
  1057. dragging: draggingNodeKey !== null,
  1058. indent: indent.value,
  1059. direction,
  1060. dropIndicatorRender,
  1061. loadData,
  1062. filterTreeNode,
  1063. onNodeClick,
  1064. onNodeDoubleClick,
  1065. onNodeExpand,
  1066. onNodeSelect,
  1067. onNodeCheck,
  1068. onNodeLoad,
  1069. onNodeMouseEnter,
  1070. onNodeMouseLeave,
  1071. onNodeContextMenu,
  1072. onNodeDragStart,
  1073. onNodeDragEnter,
  1074. onNodeDragOver,
  1075. onNodeDragLeave,
  1076. onNodeDragEnd,
  1077. onNodeDrop,
  1078. slots
  1079. }
  1080. }, {
  1081. default: () => [_createVNode("div", {
  1082. "role": "tree",
  1083. "class": classNames(prefixCls, className, rootClassName, {
  1084. [`${prefixCls}-show-line`]: showLine,
  1085. [`${prefixCls}-focused`]: focused.value,
  1086. [`${prefixCls}-active-focused`]: activeKey.value !== null
  1087. }),
  1088. "style": rootStyle
  1089. }, [_createVNode(NodeList, _objectSpread({
  1090. "ref": listRef,
  1091. "prefixCls": prefixCls,
  1092. "style": style,
  1093. "disabled": disabled,
  1094. "selectable": selectable,
  1095. "checkable": !!checkable,
  1096. "motion": motion,
  1097. "height": height,
  1098. "itemHeight": itemHeight,
  1099. "virtual": virtual,
  1100. "focusable": focusable,
  1101. "focused": focused.value,
  1102. "tabindex": tabindex,
  1103. "activeItem": activeItem.value,
  1104. "onFocus": onFocus,
  1105. "onBlur": onBlur,
  1106. "onKeydown": onKeydown,
  1107. "onActiveChange": onActiveChange,
  1108. "onListChangeStart": onListChangeStart,
  1109. "onListChangeEnd": onListChangeEnd,
  1110. "onContextmenu": onContextmenu,
  1111. "onScroll": onScroll
  1112. }, domProps), null)])]
  1113. });
  1114. };
  1115. }
  1116. });