timeline-view.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. import Ember from 'ember';
  2. import Converter from 'yarn-ui/utils/converter';
  3. export default Ember.Component.extend({
  4. canvas: {
  5. svg: undefined,
  6. h: 0,
  7. w: 0,
  8. tooltip: undefined
  9. },
  10. clusterMetrics: undefined,
  11. modelArr: [],
  12. colors: d3.scale.category10().range(),
  13. _selected: undefined,
  14. selected: function() {
  15. return this._selected;
  16. }.property(),
  17. tableComponentName: function() {
  18. return "app-attempt-table";
  19. }.property(),
  20. setSelected: function(d) {
  21. if (this._selected == d) {
  22. return;
  23. }
  24. // restore color
  25. if (this._selected) {
  26. var dom = d3.select("#timeline-bar-" + this._selected.get("id"));
  27. dom.attr("fill", this.colors[0]);
  28. }
  29. this._selected = d;
  30. this.set("selected", d);
  31. dom = d3.select("#timeline-bar-" + d.get("id"));
  32. dom.attr("fill", this.colors[1]);
  33. },
  34. getPerItemHeight: function() {
  35. var arrSize = this.modelArr.length;
  36. if (arrSize < 20) {
  37. return 30;
  38. } else if (arrSize < 100) {
  39. return 10;
  40. } else {
  41. return 2;
  42. }
  43. },
  44. getPerItemGap: function() {
  45. var arrSize = this.modelArr.length;
  46. if (arrSize < 20) {
  47. return 5;
  48. } else if (arrSize < 100) {
  49. return 1;
  50. } else {
  51. return 1;
  52. }
  53. },
  54. getCanvasHeight: function() {
  55. return (this.getPerItemHeight() + this.getPerItemGap()) * this.modelArr.length + 200;
  56. },
  57. draw: function(start, end) {
  58. // get w/h of the svg
  59. var bbox = d3.select("#" + this.get("parent-id"))
  60. .node()
  61. .getBoundingClientRect();
  62. this.canvas.w = bbox.width;
  63. this.canvas.h = this.getCanvasHeight();
  64. this.canvas.svg = d3.select("#" + this.get("parent-id"))
  65. .append("svg")
  66. .attr("width", this.canvas.w)
  67. .attr("height", this.canvas.h)
  68. .attr("id", this.get("my-id"));
  69. this.renderTimeline(start, end);
  70. },
  71. renderTimeline: function(start, end) {
  72. var border = 30;
  73. var singleBarHeight = this.getPerItemHeight();
  74. var gap = this.getPerItemGap();
  75. var textWidth = 50;
  76. /*
  77. start-time end-time
  78. |--------------------------------------|
  79. ==============
  80. ==============
  81. ==============
  82. ===============
  83. */
  84. var xScaler = d3.scale.linear()
  85. .domain([start, end])
  86. .range([0, this.canvas.w - 2 * border - textWidth]);
  87. /*
  88. * Render frame of timeline view
  89. */
  90. this.canvas.svg.append("line")
  91. .attr("x1", border + textWidth)
  92. .attr("y1", border - 5)
  93. .attr("x2", this.canvas.w - border)
  94. .attr("y2", border - 5)
  95. .attr("class", "chart");
  96. this.canvas.svg.append("line")
  97. .attr("x1", border + textWidth)
  98. .attr("y1", border - 10)
  99. .attr("x2", border + textWidth)
  100. .attr("y2", border - 5)
  101. .attr("class", "chart");
  102. this.canvas.svg.append("line")
  103. .attr("x1", this.canvas.w - border)
  104. .attr("y1", border - 10)
  105. .attr("x2", this.canvas.w - border)
  106. .attr("y2", border - 5)
  107. .attr("class", "chart");
  108. this.canvas.svg.append("text")
  109. .text(Converter.timeStampToDate(start))
  110. .attr("y", border - 15)
  111. .attr("x", border + textWidth)
  112. .attr("class", "bar-chart-text")
  113. .attr("text-anchor", "left");
  114. this.canvas.svg.append("text")
  115. .text(Converter.timeStampToDate(end))
  116. .attr("y", border - 15)
  117. .attr("x", this.canvas.w - border)
  118. .attr("class", "bar-chart-text")
  119. .attr("text-anchor", "end");
  120. // show bar
  121. var bar = this.canvas.svg.selectAll("bars")
  122. .data(this.modelArr)
  123. .enter()
  124. .append("rect")
  125. .attr("y", function(d, i) {
  126. return border + (gap + singleBarHeight) * i;
  127. })
  128. .attr("x", function(d, i) {
  129. return border + textWidth + xScaler(d.get("startTs"));
  130. })
  131. .attr("height", singleBarHeight)
  132. .attr("fill", function(d, i) {
  133. return this.colors[0];
  134. }.bind(this))
  135. .attr("width", function(d, i) {
  136. var finishedTs = xScaler(d.get("finishedTs"));
  137. finishedTs = finishedTs > 0 ? finishedTs : xScaler(end);
  138. return finishedTs - xScaler(d.get("startTs"));
  139. })
  140. .attr("id", function(d, i) {
  141. return "timeline-bar-" + d.get("id");
  142. });
  143. bar.on("click", function(d) {
  144. this.setSelected(d);
  145. }.bind(this));
  146. this.bindTooltip(bar);
  147. if (this.modelArr.length <= 20) {
  148. // show bar texts
  149. for (var i = 0; i < this.modelArr.length; i++) {
  150. this.canvas.svg.append("text")
  151. .text(this.modelArr[i].get(this.get("label")))
  152. .attr("y", border + (gap + singleBarHeight) * i + singleBarHeight / 2)
  153. .attr("x", border)
  154. .attr("class", "bar-chart-text");
  155. }
  156. }
  157. },
  158. bindTooltip: function(d) {
  159. d.on("mouseover", function(d) {
  160. this.tooltip
  161. .style("left", (d3.event.pageX) + "px")
  162. .style("top", (d3.event.pageY - 28) + "px");
  163. }.bind(this))
  164. .on("mousemove", function(d) {
  165. this.tooltip.style("opacity", .9);
  166. this.tooltip.html(d.get("tooltipLabel"))
  167. .style("left", (d3.event.pageX) + "px")
  168. .style("top", (d3.event.pageY - 28) + "px");
  169. }.bind(this))
  170. .on("mouseout", function(d) {
  171. this.tooltip.style("opacity", 0);
  172. }.bind(this));
  173. },
  174. initTooltip: function() {
  175. this.tooltip = d3.select("body")
  176. .append("div")
  177. .attr("class", "tooltip")
  178. .attr("id", "chart-tooltip")
  179. .style("opacity", 0);
  180. },
  181. didInsertElement: function() {
  182. // init tooltip
  183. this.initTooltip();
  184. // init model
  185. if (this.get("rmModel")) {
  186. this.get("rmModel").forEach(function(o) {
  187. this.modelArr.push(o);
  188. }.bind(this));
  189. }
  190. if (this.get("tsModel")) {
  191. this.get("tsModel").forEach(function(o) {
  192. this.modelArr.push(o);
  193. }.bind(this));
  194. }
  195. this.modelArr.sort(function(a, b) {
  196. var tsA = a.get("startTs");
  197. var tsB = b.get("startTs");
  198. return tsA - tsB;
  199. });
  200. if (this.modelArr.length > 0) {
  201. var begin = this.modelArr[0].get("startTs");
  202. }
  203. var end = 0;
  204. for (var i = 0; i < this.modelArr.length; i++) {
  205. var ts = this.modelArr[i].get("finishedTs");
  206. if (ts > end) {
  207. end = ts;
  208. }
  209. }
  210. if (end < begin) {
  211. end = Date.now();
  212. }
  213. this.draw(begin, end);
  214. if (this.modelArr.length > 0) {
  215. this.setSelected(this.modelArr[0]);
  216. }
  217. },
  218. });