treeUtil.ts 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  1. /**
  2. * Part of the utils function implementation process reference
  3. * https://github.com/react-component/tree/blob/master/src/util.tsx
  4. */
  5. import { difference, uniq, max, isObject, isNull, isUndefined, isEmpty, pick, get, omit } from 'lodash';
  6. import { strings } from './constants';
  7. export interface KeyEntities {
  8. [x: string]: any
  9. }
  10. export interface TreeDataSimpleJson {
  11. [x: string]: string | TreeDataSimpleJson
  12. }
  13. export interface NodeData {
  14. key: any;
  15. label: any;
  16. value: any;
  17. children?: any
  18. }
  19. export interface KeyMapProps {
  20. key?: string;
  21. label?: string;
  22. value?: string;
  23. disabled?: string;
  24. children?: string;
  25. isLeaf?: string;
  26. icon?: string
  27. }
  28. const DRAG_OFFSET = 0.45;
  29. function getPosition(level: any, index: any) {
  30. return `${level}-${index}`;
  31. }
  32. function isValid(val: any) {
  33. return !isNull(val) && !isUndefined(val);
  34. }
  35. /**
  36. * Flat nest tree data into flatten list. This is used for virtual list render.
  37. * @param treeNodeList Origin data node list
  38. * @param expandedKeys
  39. * @param filteredShownKeys
  40. * need expanded keys, provides `true` means all expanded
  41. */
  42. export function flattenTreeData(treeNodeList: any[], expandedKeys: Set<string>, keyMaps: KeyMapProps, filteredShownKeys: boolean | Set<any> = false) {
  43. const flattenList: any[] = [];
  44. const filterSearch = Boolean(filteredShownKeys);
  45. const realKeyName = get(keyMaps, 'key', 'key');
  46. const realChildrenName = get(keyMaps, 'children', 'children');
  47. if (isUndefined(treeNodeList)) {
  48. return [];
  49. }
  50. function flatten(list: any[], parent: any = null) {
  51. return list.map((treeNode, index) => {
  52. const pos = getPosition(parent ? parent.pos : '0', index);
  53. const mergedKey = treeNode[realKeyName];
  54. const otherData = {};
  55. if (keyMaps) {
  56. Object.entries(omit(keyMaps, 'children')).forEach(([key, value]) => {
  57. const result = treeNode[value as string];
  58. !isUndefined(result) && (otherData[key] = result);
  59. });
  60. }
  61. // Add FlattenDataNode into list
  62. const flattenNode: any = {
  63. ...pick(treeNode, ['key', 'label', 'value', 'icon', 'disabled', 'isLeaf']),
  64. ...otherData,
  65. parent,
  66. pos,
  67. children: null,
  68. data: treeNode,
  69. _innerDataTag: true,
  70. isEnd: [...(parent ? parent.isEnd : []), index === list.length - 1],
  71. };
  72. const isBooleanFilteredShownKeys = typeof filteredShownKeys === 'boolean';
  73. if (!filterSearch || (!isBooleanFilteredShownKeys && filteredShownKeys.has(mergedKey))) {
  74. flattenList.push(flattenNode);
  75. }
  76. // Loop treeNode children
  77. if (expandedKeys.has(mergedKey) && (!filterSearch || (!isBooleanFilteredShownKeys && filteredShownKeys.has(mergedKey)))) {
  78. flattenNode.children = flatten(treeNode[realChildrenName] || [], flattenNode);
  79. } else {
  80. flattenNode.children = [];
  81. }
  82. return flattenNode;
  83. });
  84. }
  85. flatten(treeNodeList);
  86. return flattenList;
  87. }
  88. export function convertJsonToData(treeJson: TreeDataSimpleJson) {
  89. const treeData: any[] = [];
  90. const traverseNode = (key: string, children: any, path: any, res: any[]) => {
  91. const currPath = [...path, key];
  92. const itemKey = currPath.join(strings.JSON_KEY_SPLIT);
  93. const newNode: NodeData = {
  94. key: itemKey,
  95. label: key,
  96. value: children,
  97. };
  98. if (isObject(children)) {
  99. const newChildren: any[] = [];
  100. Object.entries(children).forEach(c => {
  101. traverseNode(c[0], c[1], currPath, newChildren);
  102. });
  103. newNode.children = newChildren;
  104. }
  105. res.push(newNode);
  106. };
  107. Object.entries(treeJson).forEach(item => traverseNode(item[0], item[1], [], treeData));
  108. return treeData;
  109. }
  110. /**
  111. * Traverse all the data by `treeData`.
  112. */
  113. export function traverseDataNodes(treeNodes: any[], callback: (data: any) => void, keyMaps: KeyMapProps) {
  114. const realKeyName = get(keyMaps, 'key', 'key');
  115. const realChildrenName = get(keyMaps, 'children', 'children');
  116. const processNode = (node: any, ind?: number, parent?: any) => {
  117. const children = node ? node[realChildrenName] : treeNodes;
  118. const pos = node ? getPosition(parent.pos, ind) : '0';
  119. // Process node if is not root
  120. if (node) {
  121. const nodeKey = get(node, realKeyName, null);
  122. const data = {
  123. data: { ...node },
  124. ind,
  125. pos,
  126. key: nodeKey !== null ? nodeKey : pos,
  127. parentPos: parent.node ? parent.pos : null,
  128. level: Number(parent.level) + 1,
  129. };
  130. callback(data);
  131. }
  132. // Process children node
  133. if (children) {
  134. children.forEach((subNode: any, subIndex: number) => {
  135. processNode(subNode, subIndex, {
  136. node,
  137. pos,
  138. level: parent ? Number(parent.level) + 1 : -1,
  139. });
  140. });
  141. }
  142. };
  143. processNode(null);
  144. }
  145. /* Convert data to entities map */
  146. export function convertDataToEntities(dataNodes: any[], keyMaps?: KeyMapProps) {
  147. const posEntities = {};
  148. const keyEntities = {};
  149. const valueEntities = {};
  150. const wrapper = {
  151. posEntities,
  152. keyEntities,
  153. valueEntities,
  154. };
  155. const realValueName = get(keyMaps, 'value', 'value');
  156. traverseDataNodes(dataNodes, (data: any) => {
  157. const { pos, key, parentPos } = data;
  158. const entity = { ...data };
  159. const value = get(entity, `data.${realValueName}`, null);
  160. if (value !== null) {
  161. valueEntities[value] = key;
  162. }
  163. posEntities[pos] = entity;
  164. keyEntities[key] = entity;
  165. // Fill children
  166. entity.parent = posEntities[parentPos];
  167. if (entity.parent) {
  168. entity.parent.children = entity.parent.children || [];
  169. entity.parent.children.push(entity);
  170. }
  171. }, keyMaps);
  172. return wrapper;
  173. }
  174. /* Get key by value */
  175. export function findKeysForValues(valueList: any, valueEntities: any, isMultiple = false) {
  176. if (!isValid(valueList)) {
  177. return [];
  178. }
  179. if (!isMultiple && Array.isArray(valueList)) {
  180. valueList = valueList.length ? [valueList[0]] : [];
  181. } else if (!Array.isArray(valueList)) {
  182. valueList = [valueList];
  183. }
  184. if (isEmpty(valueEntities)) {
  185. return valueList;
  186. }
  187. const res: any[] = [];
  188. valueList.forEach((val: string) => {
  189. if (val in valueEntities) {
  190. res.push(valueEntities[val]);
  191. } else {
  192. // if val not in valueEntities, then value push to keys array
  193. val && res.push(val);
  194. }
  195. });
  196. return res;
  197. }
  198. export function findDescendantKeys(selectedKeys: string[], options: KeyEntities, self = true) {
  199. const res: string[] = [];
  200. const findChild = (item: any) => {
  201. if (!item) {
  202. return;
  203. }
  204. const { children } = item;
  205. const hasChildren = isValid(children);
  206. if (hasChildren) {
  207. children.forEach((child: any) => {
  208. res.push(child.key);
  209. findChild(options[child.key]);
  210. });
  211. }
  212. };
  213. selectedKeys.forEach(item => {
  214. if (self) {
  215. res.push(item);
  216. }
  217. findChild(options[item]);
  218. });
  219. return res;
  220. }
  221. export function findChildKeys(keys: string[], options: any, omitKeys: any[] = []) {
  222. const res: any[] = [];
  223. keys &&
  224. keys.forEach(key => {
  225. const opts = options[key];
  226. opts &&
  227. opts.children &&
  228. opts.children.forEach((child: any) => {
  229. if (!omitKeys.length || !omitKeys.includes(child.key)) {
  230. res.push(child.key);
  231. }
  232. });
  233. });
  234. return res;
  235. }
  236. /* istanbul ignore next */
  237. export function findLeafKeys(keys: string[], options: any) {
  238. const res: any[] = [];
  239. const findChild = (item: any) => {
  240. if (!item) {
  241. return;
  242. }
  243. const { children } = item;
  244. const isLeaf = !isValid(children);
  245. if (isLeaf) {
  246. res.push(item.key);
  247. } else {
  248. children.forEach((child: any) => {
  249. findChild(options[child.key]);
  250. });
  251. }
  252. };
  253. keys.forEach(item => {
  254. findChild(options[item]);
  255. });
  256. return res;
  257. }
  258. export function findSiblingKeys(selectedKeys: string[], options: any, self = true) {
  259. const par: any[] = [];
  260. selectedKeys.forEach(item => {
  261. if (options[item] && options[item].parent) {
  262. par.push(options[item].parent.key);
  263. }
  264. });
  265. const res = findChildKeys(uniq(par), options, self ? [] : selectedKeys);
  266. return res;
  267. }
  268. export function findAncestorKeys(selectedKeys: string[], options: any, self = true) {
  269. const res: any[] = [];
  270. // Recursively find the parent element
  271. const findPar = (item: any) => {
  272. if (item.parent) {
  273. res.push(item.parent.key);
  274. findPar(item.parent);
  275. }
  276. };
  277. selectedKeys.forEach(item => {
  278. options[item] && findPar(options[item]);
  279. if (self) {
  280. res.push(item);
  281. }
  282. });
  283. return res;
  284. }
  285. function getSortedKeyList(keyList: any[], keyEntities: KeyEntities) {
  286. const levelMap = {};
  287. keyList.forEach(key => {
  288. if (!keyEntities[key]) {
  289. return;
  290. }
  291. const { level } = keyEntities[key];
  292. if (levelMap[level]) {
  293. levelMap[level].push(key);
  294. } else {
  295. levelMap[level] = [key];
  296. }
  297. });
  298. return levelMap;
  299. }
  300. export function calcCheckedKeys(values: any, keyEntities: KeyEntities) {
  301. const keyList = Array.isArray(values) ? values : [values];
  302. const descendantKeys = findDescendantKeys(keyList, keyEntities, true);
  303. /**
  304. * Recursively find the parent element. Because the incoming nodes are all checked,
  305. * their descendants must be checked. That is to say, if the descendant nodes have
  306. * disabled+unchecked nodes, their ancestor nodes will definitely not be checked
  307. */
  308. const checkedKeys = new Set([...descendantKeys]);
  309. let halfCheckedKeys = new Set([]);
  310. let visited: any[] = [];
  311. const levelMap: { [key: number]: string[] } = getSortedKeyList(keyList, keyEntities);
  312. const calcCurrLevel = (node: any) => {
  313. const { key, parent, level } = node;
  314. // If the node does not have a parent node, or the node has been processed just now, no processing is done
  315. if (!parent || visited.includes(key)) {
  316. return;
  317. }
  318. const siblingKeys = findSiblingKeys([key], keyEntities);
  319. // visited for caching to avoid double counting
  320. visited = [...visited, ...siblingKeys];
  321. const allChecked = siblingKeys.every((siblingKey: string) => checkedKeys.has(siblingKey));
  322. if (!allChecked) {
  323. const ancestorKeys = findAncestorKeys([key], keyEntities, false);
  324. halfCheckedKeys = new Set([...halfCheckedKeys, ...ancestorKeys]);
  325. } else {
  326. checkedKeys.add(parent.key);
  327. // IMPORTANT! parent level may not exist in original level map; if add to the end directly may destroy the hierarchical order
  328. if (level - 1 in levelMap && level) {
  329. levelMap[level - 1].push(parent.key);
  330. } else {
  331. levelMap[level - 1] = [parent.key];
  332. }
  333. }
  334. };
  335. // Loop keyList from deepest Level to topLevel, bottom up
  336. while (!isEmpty(levelMap)) {
  337. const maxLevel = max(Object.keys(levelMap).map(key => Number(key)));
  338. levelMap[maxLevel].forEach((key: string) => calcCurrLevel(keyEntities[key]));
  339. delete levelMap[maxLevel];
  340. }
  341. return {
  342. checkedKeys,
  343. halfCheckedKeys,
  344. };
  345. }
  346. /* Calculate the expanded node by key */
  347. export function calcExpandedKeys(keyList: any[] = [], keyEntities: KeyEntities, autoExpandParent = true) {
  348. if (!Array.isArray(keyList)) {
  349. keyList = [keyList];
  350. }
  351. if (autoExpandParent) {
  352. const ancestorKeys = findAncestorKeys(keyList, keyEntities, true);
  353. return new Set(ancestorKeys);
  354. }
  355. return new Set(keyList);
  356. }
  357. /* Calculate the expanded node by value */
  358. export function calcExpandedKeysForValues(value: any, keyEntities: KeyEntities, isMultiple: boolean, valueEntities: any) {
  359. const keys = findKeysForValues(value, valueEntities, isMultiple);
  360. return new Set(findAncestorKeys(keys, keyEntities, false));
  361. }
  362. export function calcMotionKeys(oldKeySet: Set<string>, newKeySet: Set<string>, keyEntities: KeyEntities) {
  363. let motionType = 'show';
  364. const oldKeys = [...oldKeySet];
  365. const newKeys = [...newKeySet];
  366. if (Math.abs(oldKeys.length - newKeys.length) !== 1) {
  367. return { motionType, motionKeys: [] };
  368. }
  369. let diffKeys = [];
  370. if (oldKeys.length > newKeys.length) {
  371. motionType = 'hide';
  372. diffKeys = difference(oldKeys, newKeys);
  373. } else {
  374. diffKeys = difference(newKeys, oldKeys);
  375. }
  376. return {
  377. motionType: diffKeys.length === 1 ? motionType : 'show',
  378. motionKeys: diffKeys.length === 1 ? findDescendantKeys(diffKeys, keyEntities, false) : [],
  379. };
  380. }
  381. /**
  382. * @returns whether option includes sugInput.
  383. * When filterTreeNode is a function,returns the result of filterTreeNode which called with (sugInput, target, option).
  384. */
  385. export function filter(sugInput: string, option: any, filterTreeNode: any, filterProps: any) {
  386. if (!filterTreeNode) {
  387. return true;
  388. }
  389. let filterFn = filterTreeNode;
  390. let target = option;
  391. if (typeof filterTreeNode === 'boolean') {
  392. filterFn = (targetVal: string, val: any) => {
  393. const input = targetVal.toLowerCase();
  394. return val
  395. ?.toString()
  396. .toLowerCase()
  397. .includes(input);
  398. };
  399. }
  400. if (filterProps) {
  401. target = option[filterProps];
  402. }
  403. return filterFn(sugInput, target, option);
  404. }
  405. export function normalizedArr(val: any) {
  406. if (!Array.isArray(val)) {
  407. return [val];
  408. } else {
  409. return val;
  410. }
  411. }
  412. // flag is used to determine whether to return when the key does not belong to the keys in keyEntities
  413. // export function normalizeKeyList(keyList: any, keyEntities: KeyEntities, leafOnly = false) {
  414. export function normalizeKeyList(keyList: any, keyEntities: KeyEntities, leafOnly = false, flag?: boolean) {
  415. const res: string[] = [];
  416. const keyListSet = new Set(keyList);
  417. if (!leafOnly) {
  418. keyList.forEach((key: string) => {
  419. if (!keyEntities[key]) {
  420. if (flag) {
  421. res.push(key);
  422. }
  423. return;
  424. }
  425. const { parent } = keyEntities[key];
  426. if (parent && keyListSet.has(parent.key)) {
  427. return;
  428. }
  429. res.push(key);
  430. });
  431. } else {
  432. keyList.forEach(key => {
  433. if (keyEntities[key] && !isValid(keyEntities[key].children)) {
  434. res.push(key);
  435. }
  436. // when key is not in keyEntities, if flag is true, key should be push in res
  437. if (!keyEntities[key] && flag) {
  438. res.push(key);
  439. }
  440. });
  441. }
  442. return res;
  443. }
  444. export function getMotionKeys(eventKey: string, expandedKeys: Set<string>, keyEntities: KeyEntities) {
  445. const res: any[] = [];
  446. const getChild = (itemKey: string) => {
  447. keyEntities[itemKey].children &&
  448. keyEntities[itemKey].children.forEach((item: any) => {
  449. const { key } = item;
  450. res.push(key);
  451. if (expandedKeys.has(key)) {
  452. getChild(key);
  453. }
  454. });
  455. };
  456. getChild(eventKey);
  457. return res;
  458. }
  459. export function calcCheckedKeysForChecked(key: string, keyEntities: KeyEntities, checkedKeys: Set<string>, halfCheckedKeys: Set<string>) {
  460. const descendantKeys = findDescendantKeys([key], keyEntities, true);
  461. const nodeItem = keyEntities[key];
  462. checkedKeys = new Set([...checkedKeys, key]);
  463. const calcCurrLevel = (node: any) => {
  464. if (!node.parent) {
  465. return;
  466. }
  467. const { key } = node;
  468. const siblingKeys = findSiblingKeys([key], keyEntities);
  469. const allChecked = siblingKeys.every(key => checkedKeys.has(key));
  470. if (!allChecked) {
  471. const ancestorKeys = findAncestorKeys([key], keyEntities, false);
  472. halfCheckedKeys = new Set([...halfCheckedKeys, ...ancestorKeys]);
  473. } else {
  474. const par = node.parent;
  475. checkedKeys.add(par.key);
  476. calcCurrLevel(par);
  477. }
  478. };
  479. calcCurrLevel(nodeItem);
  480. return {
  481. checkedKeys: new Set([...checkedKeys, ...descendantKeys]),
  482. halfCheckedKeys,
  483. };
  484. }
  485. export function calcCheckedKeysForUnchecked(key: string, keyEntities: KeyEntities, checkedKeys: Set<string>, halfCheckedKeys: Set<string>) {
  486. const descendantKeys = findDescendantKeys([key], keyEntities, true);
  487. const nodeItem = keyEntities[key];
  488. descendantKeys.forEach(descendantKey => {
  489. if (checkedKeys.has(descendantKey)) {
  490. checkedKeys.delete(descendantKey);
  491. }
  492. if (halfCheckedKeys.has(descendantKey)) {
  493. halfCheckedKeys.delete(descendantKey);
  494. }
  495. });
  496. const calcCurrLevel = (node: any) => {
  497. const par = node.parent;
  498. // no parent
  499. if (!par) {
  500. return;
  501. }
  502. // Has a parent node, and the parent node is not checked or halfChecked
  503. if (!checkedKeys.has(par.key) && !halfCheckedKeys.has(par.key)) {
  504. return;
  505. }
  506. // Has a parent node, and the parent node is checked or halfChecked
  507. const { key } = node;
  508. const siblingKeys = findSiblingKeys([key], keyEntities);
  509. const anyChecked = siblingKeys.some(key => checkedKeys.has(key) || halfCheckedKeys.has(key));
  510. const ancestorKeys = findAncestorKeys([key], keyEntities, false);
  511. // If there is checked or halfChecked in the sibling node, you need to change the parent node to halfChecked
  512. if (anyChecked) {
  513. ancestorKeys.forEach(itemKey => {
  514. if (checkedKeys.has(itemKey)) {
  515. checkedKeys.delete(itemKey);
  516. halfCheckedKeys.add(itemKey);
  517. }
  518. });
  519. // If there is no checked or halfChecked in the sibling node, you need to change the parent node to unchecked
  520. } else {
  521. if (checkedKeys.has(par.key)) {
  522. checkedKeys.delete(par.key);
  523. }
  524. if (halfCheckedKeys.has(par.key)) {
  525. halfCheckedKeys.delete(par.key);
  526. }
  527. calcCurrLevel(par);
  528. }
  529. };
  530. nodeItem && calcCurrLevel(nodeItem);
  531. return {
  532. checkedKeys,
  533. halfCheckedKeys,
  534. };
  535. }
  536. export function filterTreeData(info: any) {
  537. const {
  538. showFilteredOnly,
  539. keyEntities,
  540. inputValue,
  541. treeData,
  542. filterTreeNode,
  543. filterProps,
  544. prevExpandedKeys,
  545. keyMaps
  546. } = info;
  547. let filteredOptsKeys = [];
  548. filteredOptsKeys = Object.values(keyEntities)
  549. .filter((item: any) => filter(inputValue, item.data, filterTreeNode, filterProps))
  550. .map((item: any) => item.key);
  551. let expandedOptsKeys = findAncestorKeys(filteredOptsKeys, keyEntities, false);
  552. if (prevExpandedKeys.length) {
  553. const prevExpandedValidKeys = prevExpandedKeys.filter((key: string) => Boolean(keyEntities[key]));
  554. expandedOptsKeys = expandedOptsKeys.concat(prevExpandedValidKeys);
  555. }
  556. const shownChildKeys = findDescendantKeys(filteredOptsKeys, keyEntities, true);
  557. const filteredShownKeys = new Set([...shownChildKeys, ...expandedOptsKeys]);
  558. const flattenNodes = flattenTreeData(treeData, new Set(expandedOptsKeys), keyMaps, showFilteredOnly && filteredShownKeys);
  559. return {
  560. flattenNodes,
  561. filteredKeys: new Set(filteredOptsKeys),
  562. filteredExpandedKeys: new Set(expandedOptsKeys),
  563. filteredShownKeys,
  564. };
  565. }
  566. // return data.value if data.value exist else fall back to key
  567. export function getValueOrKey(data: any, keyMaps?: KeyMapProps) {
  568. const valueName = get(keyMaps, 'value', 'value');
  569. const keyName = get(keyMaps, 'key', 'key');
  570. if (Array.isArray(data)) {
  571. return data.map(item => get(item, valueName, item[keyName]));
  572. }
  573. return get(data, valueName, data[keyName]);
  574. }
  575. /* Convert value to string */
  576. export function normalizeValue(value: any, withObject: boolean, keyMaps?: KeyMapProps) {
  577. if (withObject && isValid(value)) {
  578. return getValueOrKey(value, keyMaps);
  579. } else {
  580. return value;
  581. }
  582. }
  583. export function updateKeys(keySet: Set<string> | string[], keyEntities: KeyEntities) {
  584. const keyArr = [...keySet];
  585. return keyArr.filter(key => key in keyEntities);
  586. }
  587. export function calcDisabledKeys(keyEntities: KeyEntities, keyMaps?: KeyMapProps) {
  588. const disabledName = get(keyMaps, 'disabled', 'disabled');
  589. const disabledKeys = Object.keys(keyEntities).filter(key => keyEntities[key].data[disabledName]);
  590. const { checkedKeys } = calcCheckedKeys(disabledKeys, keyEntities);
  591. return checkedKeys;
  592. }
  593. export function calcDropRelativePosition(event: any, treeNode: any) {
  594. const { clientY } = event;
  595. const { top, bottom, height } = treeNode.nodeInstance.getBoundingClientRect();
  596. // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
  597. if (clientY <= top + height * DRAG_OFFSET) {
  598. return -1;
  599. }
  600. if (clientY >= bottom - height * DRAG_OFFSET) {
  601. return 1;
  602. }
  603. return 0;
  604. }
  605. export function getDragNodesKeys(key: string, keyEntities: KeyEntities) {
  606. return findDescendantKeys([key], keyEntities, true);
  607. }
  608. export function calcDropActualPosition(pos: string, relativeDropPos: any) {
  609. const posArr = pos.split('-');
  610. // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
  611. return relativeDropPos + Number(posArr[posArr.length - 1]);
  612. }