treeUtil.js 11 KB

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