conductUtil.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. import { note } from '../../vc-util/warning';
  2. function removeFromCheckedKeys(halfCheckedKeys, checkedKeys) {
  3. const filteredKeys = new Set();
  4. halfCheckedKeys.forEach(key => {
  5. if (!checkedKeys.has(key)) {
  6. filteredKeys.add(key);
  7. }
  8. });
  9. return filteredKeys;
  10. }
  11. export function isCheckDisabled(node) {
  12. const {
  13. disabled,
  14. disableCheckbox,
  15. checkable
  16. } = node || {};
  17. return !!(disabled || disableCheckbox) || checkable === false;
  18. }
  19. // Fill miss keys
  20. function fillConductCheck(keys, levelEntities, maxLevel, syntheticGetCheckDisabled) {
  21. const checkedKeys = new Set(keys);
  22. const halfCheckedKeys = new Set();
  23. // Add checked keys top to bottom
  24. for (let level = 0; level <= maxLevel; level += 1) {
  25. const entities = levelEntities.get(level) || new Set();
  26. entities.forEach(entity => {
  27. const {
  28. key,
  29. node,
  30. children = []
  31. } = entity;
  32. if (checkedKeys.has(key) && !syntheticGetCheckDisabled(node)) {
  33. children.filter(childEntity => !syntheticGetCheckDisabled(childEntity.node)).forEach(childEntity => {
  34. checkedKeys.add(childEntity.key);
  35. });
  36. }
  37. });
  38. }
  39. // Add checked keys from bottom to top
  40. const visitedKeys = new Set();
  41. for (let level = maxLevel; level >= 0; level -= 1) {
  42. const entities = levelEntities.get(level) || new Set();
  43. entities.forEach(entity => {
  44. const {
  45. parent,
  46. node
  47. } = entity;
  48. // Skip if no need to check
  49. if (syntheticGetCheckDisabled(node) || !entity.parent || visitedKeys.has(entity.parent.key)) {
  50. return;
  51. }
  52. // Skip if parent is disabled
  53. if (syntheticGetCheckDisabled(entity.parent.node)) {
  54. visitedKeys.add(parent.key);
  55. return;
  56. }
  57. let allChecked = true;
  58. let partialChecked = false;
  59. (parent.children || []).filter(childEntity => !syntheticGetCheckDisabled(childEntity.node)).forEach(_ref => {
  60. let {
  61. key
  62. } = _ref;
  63. const checked = checkedKeys.has(key);
  64. if (allChecked && !checked) {
  65. allChecked = false;
  66. }
  67. if (!partialChecked && (checked || halfCheckedKeys.has(key))) {
  68. partialChecked = true;
  69. }
  70. });
  71. if (allChecked) {
  72. checkedKeys.add(parent.key);
  73. }
  74. if (partialChecked) {
  75. halfCheckedKeys.add(parent.key);
  76. }
  77. visitedKeys.add(parent.key);
  78. });
  79. }
  80. return {
  81. checkedKeys: Array.from(checkedKeys),
  82. halfCheckedKeys: Array.from(removeFromCheckedKeys(halfCheckedKeys, checkedKeys))
  83. };
  84. }
  85. // Remove useless key
  86. function cleanConductCheck(keys, halfKeys, levelEntities, maxLevel, syntheticGetCheckDisabled) {
  87. const checkedKeys = new Set(keys);
  88. let halfCheckedKeys = new Set(halfKeys);
  89. // Remove checked keys from top to bottom
  90. for (let level = 0; level <= maxLevel; level += 1) {
  91. const entities = levelEntities.get(level) || new Set();
  92. entities.forEach(entity => {
  93. const {
  94. key,
  95. node,
  96. children = []
  97. } = entity;
  98. if (!checkedKeys.has(key) && !halfCheckedKeys.has(key) && !syntheticGetCheckDisabled(node)) {
  99. children.filter(childEntity => !syntheticGetCheckDisabled(childEntity.node)).forEach(childEntity => {
  100. checkedKeys.delete(childEntity.key);
  101. });
  102. }
  103. });
  104. }
  105. // Remove checked keys form bottom to top
  106. halfCheckedKeys = new Set();
  107. const visitedKeys = new Set();
  108. for (let level = maxLevel; level >= 0; level -= 1) {
  109. const entities = levelEntities.get(level) || new Set();
  110. entities.forEach(entity => {
  111. const {
  112. parent,
  113. node
  114. } = entity;
  115. // Skip if no need to check
  116. if (syntheticGetCheckDisabled(node) || !entity.parent || visitedKeys.has(entity.parent.key)) {
  117. return;
  118. }
  119. // Skip if parent is disabled
  120. if (syntheticGetCheckDisabled(entity.parent.node)) {
  121. visitedKeys.add(parent.key);
  122. return;
  123. }
  124. let allChecked = true;
  125. let partialChecked = false;
  126. (parent.children || []).filter(childEntity => !syntheticGetCheckDisabled(childEntity.node)).forEach(_ref2 => {
  127. let {
  128. key
  129. } = _ref2;
  130. const checked = checkedKeys.has(key);
  131. if (allChecked && !checked) {
  132. allChecked = false;
  133. }
  134. if (!partialChecked && (checked || halfCheckedKeys.has(key))) {
  135. partialChecked = true;
  136. }
  137. });
  138. if (!allChecked) {
  139. checkedKeys.delete(parent.key);
  140. }
  141. if (partialChecked) {
  142. halfCheckedKeys.add(parent.key);
  143. }
  144. visitedKeys.add(parent.key);
  145. });
  146. }
  147. return {
  148. checkedKeys: Array.from(checkedKeys),
  149. halfCheckedKeys: Array.from(removeFromCheckedKeys(halfCheckedKeys, checkedKeys))
  150. };
  151. }
  152. /**
  153. * Conduct with keys.
  154. * @param keyList current key list
  155. * @param keyEntities key - dataEntity map
  156. * @param mode `fill` to fill missing key, `clean` to remove useless key
  157. */
  158. export function conductCheck(keyList, checked, keyEntities, maxLevel, levelEntities, getCheckDisabled) {
  159. const warningMissKeys = [];
  160. let syntheticGetCheckDisabled;
  161. if (getCheckDisabled) {
  162. syntheticGetCheckDisabled = getCheckDisabled;
  163. } else {
  164. syntheticGetCheckDisabled = isCheckDisabled;
  165. }
  166. // We only handle exist keys
  167. const keys = new Set(keyList.filter(key => {
  168. const hasEntity = !!keyEntities[key];
  169. if (!hasEntity) {
  170. warningMissKeys.push(key);
  171. }
  172. return hasEntity;
  173. }));
  174. note(!warningMissKeys.length, `Tree missing follow keys: ${warningMissKeys.slice(0, 100).map(key => `'${key}'`).join(', ')}`);
  175. let result;
  176. if (checked === true) {
  177. result = fillConductCheck(keys, levelEntities, maxLevel, syntheticGetCheckDisabled);
  178. } else {
  179. result = cleanConductCheck(keys, checked.halfCheckedKeys, levelEntities, maxLevel, syntheticGetCheckDisabled);
  180. }
  181. return result;
  182. }