treeUtil.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. import _typeof from "@babel/runtime/helpers/esm/typeof";
  2. import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
  3. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  4. import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
  5. var _excluded = ["children"];
  6. import toArray from "rc-util/es/Children/toArray";
  7. import omit from "rc-util/es/omit";
  8. import warning from "rc-util/es/warning";
  9. import getEntity from "./keyUtil";
  10. export function getPosition(level, index) {
  11. return "".concat(level, "-").concat(index);
  12. }
  13. export function isTreeNode(node) {
  14. return node && node.type && node.type.isTreeNode;
  15. }
  16. export function getKey(key, pos) {
  17. if (key !== null && key !== undefined) {
  18. return key;
  19. }
  20. return pos;
  21. }
  22. export function fillFieldNames(fieldNames) {
  23. var _ref = fieldNames || {},
  24. title = _ref.title,
  25. _title = _ref._title,
  26. key = _ref.key,
  27. children = _ref.children;
  28. var mergedTitle = title || 'title';
  29. return {
  30. title: mergedTitle,
  31. _title: _title || [mergedTitle],
  32. key: key || 'key',
  33. children: children || 'children'
  34. };
  35. }
  36. /**
  37. * Warning if TreeNode do not provides key
  38. */
  39. export function warningWithoutKey(treeData, fieldNames) {
  40. var keys = new Map();
  41. function dig(list) {
  42. var path = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
  43. (list || []).forEach(function (treeNode) {
  44. var key = treeNode[fieldNames.key];
  45. var children = treeNode[fieldNames.children];
  46. warning(key !== null && key !== undefined, "Tree node must have a certain key: [".concat(path).concat(key, "]"));
  47. var recordKey = String(key);
  48. warning(!keys.has(recordKey) || key === null || key === undefined, "Same 'key' exist in the Tree: ".concat(recordKey));
  49. keys.set(recordKey, true);
  50. dig(children, "".concat(path).concat(recordKey, " > "));
  51. });
  52. }
  53. dig(treeData);
  54. }
  55. /**
  56. * Convert `children` of Tree into `treeData` structure.
  57. */
  58. export function convertTreeToData(rootNodes) {
  59. function dig(node) {
  60. var treeNodes = toArray(node);
  61. return treeNodes.map(function (treeNode) {
  62. // Filter invalidate node
  63. if (!isTreeNode(treeNode)) {
  64. warning(!treeNode, 'Tree/TreeNode can only accept TreeNode as children.');
  65. return null;
  66. }
  67. var key = treeNode.key;
  68. var _treeNode$props = treeNode.props,
  69. children = _treeNode$props.children,
  70. rest = _objectWithoutProperties(_treeNode$props, _excluded);
  71. var dataNode = _objectSpread({
  72. key: key
  73. }, rest);
  74. var parsedChildren = dig(children);
  75. if (parsedChildren.length) {
  76. dataNode.children = parsedChildren;
  77. }
  78. return dataNode;
  79. }).filter(function (dataNode) {
  80. return dataNode;
  81. });
  82. }
  83. return dig(rootNodes);
  84. }
  85. /**
  86. * Flat nest tree data into flatten list. This is used for virtual list render.
  87. * @param treeNodeList Origin data node list
  88. * @param expandedKeys
  89. * need expanded keys, provides `true` means all expanded (used in `rc-tree-select`).
  90. */
  91. export function flattenTreeData(treeNodeList, expandedKeys, fieldNames) {
  92. var _fillFieldNames = fillFieldNames(fieldNames),
  93. fieldTitles = _fillFieldNames._title,
  94. fieldKey = _fillFieldNames.key,
  95. fieldChildren = _fillFieldNames.children;
  96. var expandedKeySet = new Set(expandedKeys === true ? [] : expandedKeys);
  97. var flattenList = [];
  98. function dig(list) {
  99. var parent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
  100. return list.map(function (treeNode, index) {
  101. var pos = getPosition(parent ? parent.pos : '0', index);
  102. var mergedKey = getKey(treeNode[fieldKey], pos);
  103. // Pick matched title in field title list
  104. var mergedTitle;
  105. for (var i = 0; i < fieldTitles.length; i += 1) {
  106. var fieldTitle = fieldTitles[i];
  107. if (treeNode[fieldTitle] !== undefined) {
  108. mergedTitle = treeNode[fieldTitle];
  109. break;
  110. }
  111. }
  112. // Add FlattenDataNode into list
  113. // We use `Object.assign` here to save perf since babel's `objectSpread` has perf issue
  114. var flattenNode = Object.assign(omit(treeNode, [].concat(_toConsumableArray(fieldTitles), [fieldKey, fieldChildren])), {
  115. title: mergedTitle,
  116. key: mergedKey,
  117. parent: parent,
  118. pos: pos,
  119. children: null,
  120. data: treeNode,
  121. isStart: [].concat(_toConsumableArray(parent ? parent.isStart : []), [index === 0]),
  122. isEnd: [].concat(_toConsumableArray(parent ? parent.isEnd : []), [index === list.length - 1])
  123. });
  124. flattenList.push(flattenNode);
  125. // Loop treeNode children
  126. if (expandedKeys === true || expandedKeySet.has(mergedKey)) {
  127. flattenNode.children = dig(treeNode[fieldChildren] || [], flattenNode);
  128. } else {
  129. flattenNode.children = [];
  130. }
  131. return flattenNode;
  132. });
  133. }
  134. dig(treeNodeList);
  135. return flattenList;
  136. }
  137. /**
  138. * Traverse all the data by `treeData`.
  139. * Please not use it out of the `rc-tree` since we may refactor this code.
  140. */
  141. export function traverseDataNodes(dataNodes, callback,
  142. // To avoid too many params, let use config instead of origin param
  143. config) {
  144. var mergedConfig = {};
  145. if (_typeof(config) === 'object') {
  146. mergedConfig = config;
  147. } else {
  148. mergedConfig = {
  149. externalGetKey: config
  150. };
  151. }
  152. mergedConfig = mergedConfig || {};
  153. // Init config
  154. var _mergedConfig = mergedConfig,
  155. childrenPropName = _mergedConfig.childrenPropName,
  156. externalGetKey = _mergedConfig.externalGetKey,
  157. fieldNames = _mergedConfig.fieldNames;
  158. var _fillFieldNames2 = fillFieldNames(fieldNames),
  159. fieldKey = _fillFieldNames2.key,
  160. fieldChildren = _fillFieldNames2.children;
  161. var mergeChildrenPropName = childrenPropName || fieldChildren;
  162. // Get keys
  163. var syntheticGetKey;
  164. if (externalGetKey) {
  165. if (typeof externalGetKey === 'string') {
  166. syntheticGetKey = function syntheticGetKey(node) {
  167. return node[externalGetKey];
  168. };
  169. } else if (typeof externalGetKey === 'function') {
  170. syntheticGetKey = function syntheticGetKey(node) {
  171. return externalGetKey(node);
  172. };
  173. }
  174. } else {
  175. syntheticGetKey = function syntheticGetKey(node, pos) {
  176. return getKey(node[fieldKey], pos);
  177. };
  178. }
  179. // Process
  180. function processNode(node, index, parent, pathNodes) {
  181. var children = node ? node[mergeChildrenPropName] : dataNodes;
  182. var pos = node ? getPosition(parent.pos, index) : '0';
  183. var connectNodes = node ? [].concat(_toConsumableArray(pathNodes), [node]) : [];
  184. // Process node if is not root
  185. if (node) {
  186. var key = syntheticGetKey(node, pos);
  187. var _data = {
  188. node: node,
  189. index: index,
  190. pos: pos,
  191. key: key,
  192. parentPos: parent.node ? parent.pos : null,
  193. level: parent.level + 1,
  194. nodes: connectNodes
  195. };
  196. callback(_data);
  197. }
  198. // Process children node
  199. if (children) {
  200. children.forEach(function (subNode, subIndex) {
  201. processNode(subNode, subIndex, {
  202. node: node,
  203. pos: pos,
  204. level: parent ? parent.level + 1 : -1
  205. }, connectNodes);
  206. });
  207. }
  208. }
  209. processNode(null);
  210. }
  211. /**
  212. * Convert `treeData` into entity records.
  213. */
  214. export function convertDataToEntities(dataNodes) {
  215. var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
  216. initWrapper = _ref2.initWrapper,
  217. processEntity = _ref2.processEntity,
  218. onProcessFinished = _ref2.onProcessFinished,
  219. externalGetKey = _ref2.externalGetKey,
  220. childrenPropName = _ref2.childrenPropName,
  221. fieldNames = _ref2.fieldNames;
  222. var /** @deprecated Use `config.externalGetKey` instead */
  223. legacyExternalGetKey = arguments.length > 2 ? arguments[2] : undefined;
  224. // Init config
  225. var mergedExternalGetKey = externalGetKey || legacyExternalGetKey;
  226. var posEntities = {};
  227. var keyEntities = {};
  228. var wrapper = {
  229. posEntities: posEntities,
  230. keyEntities: keyEntities
  231. };
  232. if (initWrapper) {
  233. wrapper = initWrapper(wrapper) || wrapper;
  234. }
  235. traverseDataNodes(dataNodes, function (item) {
  236. var node = item.node,
  237. index = item.index,
  238. pos = item.pos,
  239. key = item.key,
  240. parentPos = item.parentPos,
  241. level = item.level,
  242. nodes = item.nodes;
  243. var entity = {
  244. node: node,
  245. nodes: nodes,
  246. index: index,
  247. key: key,
  248. pos: pos,
  249. level: level
  250. };
  251. var mergedKey = getKey(key, pos);
  252. posEntities[pos] = entity;
  253. keyEntities[mergedKey] = entity;
  254. // Fill children
  255. entity.parent = posEntities[parentPos];
  256. if (entity.parent) {
  257. entity.parent.children = entity.parent.children || [];
  258. entity.parent.children.push(entity);
  259. }
  260. if (processEntity) {
  261. processEntity(entity, wrapper);
  262. }
  263. }, {
  264. externalGetKey: mergedExternalGetKey,
  265. childrenPropName: childrenPropName,
  266. fieldNames: fieldNames
  267. });
  268. if (onProcessFinished) {
  269. onProcessFinished(wrapper);
  270. }
  271. return wrapper;
  272. }
  273. /**
  274. * Get TreeNode props with Tree props.
  275. */
  276. export function getTreeNodeProps(key, _ref3) {
  277. var expandedKeys = _ref3.expandedKeys,
  278. selectedKeys = _ref3.selectedKeys,
  279. loadedKeys = _ref3.loadedKeys,
  280. loadingKeys = _ref3.loadingKeys,
  281. checkedKeys = _ref3.checkedKeys,
  282. halfCheckedKeys = _ref3.halfCheckedKeys,
  283. dragOverNodeKey = _ref3.dragOverNodeKey,
  284. dropPosition = _ref3.dropPosition,
  285. keyEntities = _ref3.keyEntities;
  286. var entity = getEntity(keyEntities, key);
  287. var treeNodeProps = {
  288. eventKey: key,
  289. expanded: expandedKeys.indexOf(key) !== -1,
  290. selected: selectedKeys.indexOf(key) !== -1,
  291. loaded: loadedKeys.indexOf(key) !== -1,
  292. loading: loadingKeys.indexOf(key) !== -1,
  293. checked: checkedKeys.indexOf(key) !== -1,
  294. halfChecked: halfCheckedKeys.indexOf(key) !== -1,
  295. pos: String(entity ? entity.pos : ''),
  296. // [Legacy] Drag props
  297. // Since the interaction of drag is changed, the semantic of the props are
  298. // not accuracy, I think it should be finally removed
  299. dragOver: dragOverNodeKey === key && dropPosition === 0,
  300. dragOverGapTop: dragOverNodeKey === key && dropPosition === -1,
  301. dragOverGapBottom: dragOverNodeKey === key && dropPosition === 1
  302. };
  303. return treeNodeProps;
  304. }
  305. export function convertNodePropsToEventData(props) {
  306. var data = props.data,
  307. expanded = props.expanded,
  308. selected = props.selected,
  309. checked = props.checked,
  310. loaded = props.loaded,
  311. loading = props.loading,
  312. halfChecked = props.halfChecked,
  313. dragOver = props.dragOver,
  314. dragOverGapTop = props.dragOverGapTop,
  315. dragOverGapBottom = props.dragOverGapBottom,
  316. pos = props.pos,
  317. active = props.active,
  318. eventKey = props.eventKey;
  319. var eventData = _objectSpread(_objectSpread({}, data), {}, {
  320. expanded: expanded,
  321. selected: selected,
  322. checked: checked,
  323. loaded: loaded,
  324. loading: loading,
  325. halfChecked: halfChecked,
  326. dragOver: dragOver,
  327. dragOverGapTop: dragOverGapTop,
  328. dragOverGapBottom: dragOverGapBottom,
  329. pos: pos,
  330. active: active,
  331. key: eventKey
  332. });
  333. if (!('props' in eventData)) {
  334. Object.defineProperty(eventData, 'props', {
  335. get: function get() {
  336. warning(false, 'Second param return from event is node data instead of TreeNode instance. Please read value directly instead of reading from `props`.');
  337. return props;
  338. }
  339. });
  340. }
  341. return eventData;
  342. }