treeUtil.ts 22 KB

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