tree-selector.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. import Ember from 'ember';
  19. const INBETWEEN_HEIGHT = 130;
  20. export default Ember.Component.extend({
  21. // Map: <queue-name, queue>
  22. map : undefined,
  23. // Normalized data for d3
  24. treeData: undefined,
  25. // folded queues, folded[<queue-name>] == true means <queue-name> is folded
  26. foldedQueues: { },
  27. // maxDepth
  28. maxDepth: 0,
  29. // num of leaf queue, folded queue is treated as leaf queue
  30. numOfLeafQueue: 0,
  31. // mainSvg
  32. mainSvg: undefined,
  33. // Init data
  34. initData: function() {
  35. this.map = { };
  36. this.treeData = { };
  37. this.maxDepth = 0;
  38. this.numOfLeafQueue = 0;
  39. this.get("model")
  40. .forEach(function(o) {
  41. this.map[o.id] = o;
  42. }.bind(this));
  43. // var selected = this.get("selected");
  44. this.initQueue("root", 1, this.treeData);
  45. },
  46. // get Children array of given queue
  47. getChildrenNamesArray: function(q) {
  48. var namesArr = [];
  49. // Folded queue's children is empty
  50. if (this.foldedQueues[q.get("name")]) {
  51. return namesArr;
  52. }
  53. var names = q.get("children");
  54. if (names) {
  55. names.forEach(function(name) {
  56. namesArr.push(name);
  57. });
  58. }
  59. return namesArr;
  60. },
  61. // Init queues
  62. initQueue: function(queueName, depth, node) {
  63. if ((!queueName) || (!this.map[queueName])) {
  64. // Queue is not existed
  65. return;
  66. }
  67. if (depth > this.maxDepth) {
  68. this.maxDepth = this.maxDepth + 1;
  69. }
  70. var queue = this.map[queueName];
  71. var names = this.getChildrenNamesArray(queue);
  72. node.name = queueName;
  73. node.parent = queue.get("parent");
  74. node.queueData = queue;
  75. if (names.length > 0) {
  76. node.children = [];
  77. names.forEach(function(name) {
  78. var childQueueData = {};
  79. node.children.push(childQueueData);
  80. this.initQueue(name, depth + 1, childQueueData);
  81. }.bind(this));
  82. } else {
  83. this.numOfLeafQueue = this.numOfLeafQueue + 1;
  84. }
  85. },
  86. update: function(source, root, tree, diagonal) {
  87. var duration = 300;
  88. var i = 0;
  89. // Compute the new tree layout.
  90. var nodes = tree.nodes(root).reverse();
  91. var links = tree.links(nodes);
  92. // Normalize for fixed-depth.
  93. nodes.forEach(function(d) { d.y = d.depth * 200; });
  94. // Update the nodes…
  95. var node = this.mainSvg.selectAll("g.node")
  96. .data(nodes, function(d) { return d.id || (d.id = ++i); });
  97. // Enter any new nodes at the parent's previous position.
  98. var nodeEnter = node.enter().append("g")
  99. .attr("class", "node")
  100. .attr("transform", function() { return "translate(" + source.y0 + "," + source.x0 + ")"; })
  101. .on("mouseover", function(d){
  102. if (d.queueData.get("name") !== this.get("selected")) {
  103. document.location.href = "#/yarn-queues/" + d.queueData.get("name");
  104. }
  105. Ember.run.later(this, function () {
  106. var treeWidth = this.maxDepth * 200;
  107. var treeHeight = this.numOfLeafQueue * INBETWEEN_HEIGHT;
  108. var tree = d3.layout.tree().size([treeHeight, treeWidth]);
  109. var diagonal = d3.svg.diagonal()
  110. .projection(function(d) { return [d.y, d.x]; });
  111. this.update(this.treeData, this.treeData, tree, diagonal);
  112. }, 100);
  113. }.bind(this))
  114. .on("click", function (d) {
  115. document.location.href = "#/yarn-queue/" + d.queueData.get("name");
  116. });
  117. nodeEnter.append("circle")
  118. .attr("r", 1e-6)
  119. .style("fill", function(d) {
  120. var usedCap = d.queueData.get("usedCapacity");
  121. if (usedCap <= 60.0) {
  122. return "LimeGreen";
  123. } else if (usedCap <= 100.0) {
  124. return "DarkOrange";
  125. } else {
  126. return "LightCoral";
  127. }
  128. });
  129. // append percentage
  130. nodeEnter.append("text")
  131. .attr("x", function() { return 0; })
  132. .attr("dy", ".35em")
  133. .attr("fill", "white")
  134. .attr("text-anchor", function() { return "middle"; })
  135. .text(function(d) {
  136. var usedCap = d.queueData.get("usedCapacity");
  137. if (usedCap >= 100.0) {
  138. return usedCap.toFixed(0) + "%";
  139. } else {
  140. return usedCap.toFixed(1) + "%";
  141. }
  142. })
  143. .style("fill-opacity", 1e-6);
  144. // append queue name
  145. nodeEnter.append("text")
  146. .attr("x", "0px")
  147. .attr("dy", "45px")
  148. .attr("text-anchor", "middle")
  149. .text(function(d) { return d.name; })
  150. .style("fill-opacity", 1e-6);
  151. // Transition nodes to their new position.
  152. var nodeUpdate = node.transition()
  153. .duration(duration)
  154. .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
  155. nodeUpdate.select("circle")
  156. .attr("r", 30)
  157. .attr("href",
  158. function(d) {
  159. return "#/yarn-queues/" + d.queueData.get("name");
  160. })
  161. .style("stroke-width", function(d) {
  162. if (d.queueData.get("name") === this.get("selected")) {
  163. return 7;
  164. } else {
  165. return 2;
  166. }
  167. }.bind(this))
  168. .style("stroke", function(d) {
  169. if (d.queueData.get("name") === this.get("selected")) {
  170. return "gray";
  171. } else {
  172. return "gray";
  173. }
  174. }.bind(this));
  175. nodeUpdate.selectAll("text")
  176. .style("fill-opacity", 1);
  177. // Transition exiting nodes to the parent's new position.
  178. var nodeExit = node.exit().transition()
  179. .duration(duration)
  180. .attr("transform", function() { return "translate(" + source.y + "," + source.x + ")"; })
  181. .remove();
  182. nodeExit.select("circle")
  183. .attr("r", 1e-6);
  184. nodeExit.select("text")
  185. .style("fill-opacity", 1e-6);
  186. // Update the links…
  187. var link = this.mainSvg.selectAll("path.link")
  188. .data(links, function(d) { return d.target.id; });
  189. // Enter any new links at the parent's previous position.
  190. link.enter().insert("path", "g")
  191. .attr("class", "link")
  192. .attr("d", function() {
  193. var o = {x: source.x0, y: source.y0};
  194. return diagonal({source: o, target: o});
  195. });
  196. // Transition links to their new position.
  197. link.transition()
  198. .duration(duration)
  199. .attr("d", diagonal);
  200. // Transition exiting nodes to the parent's new position.
  201. link.exit().transition()
  202. .duration(duration)
  203. .attr("d", function() {
  204. var o = {x: source.x, y: source.y};
  205. return diagonal({source: o, target: o});
  206. })
  207. .remove();
  208. // Stash the old positions for transition.
  209. nodes.forEach(function(d) {
  210. d.x0 = d.x;
  211. d.y0 = d.y;
  212. });
  213. },
  214. reDraw: function() {
  215. this.initData();
  216. var margin = {top: 20, right: 120, bottom: 20, left: 120};
  217. var treeWidth = this.maxDepth * 200;
  218. var treeHeight = this.numOfLeafQueue * INBETWEEN_HEIGHT;
  219. var width = treeWidth + margin.left + margin.right;
  220. var height = treeHeight + margin.top + margin.bottom;
  221. if (this.mainSvg) {
  222. this.mainSvg.remove();
  223. }
  224. this.mainSvg = d3.select("#" + this.get("parentId")).append("svg")
  225. .attr("width", width)
  226. .attr("height", height)
  227. .attr("class", "tree-selector")
  228. .append("g")
  229. .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  230. var tree = d3.layout.tree().size([treeHeight, treeWidth]);
  231. var diagonal = d3.svg.diagonal()
  232. .projection(function(d) { return [d.y, d.x]; });
  233. var root = this.treeData;
  234. root.x0 = height / 2;
  235. root.y0 = 0;
  236. d3.select(window.frameElement).style("height", height);
  237. this.update(root, root, tree, diagonal);
  238. },
  239. didInsertElement: function() {
  240. this.reDraw();
  241. }
  242. });