API Docs for:
Show:

File: addon/mixins/graph-graphic-with-tracking-dot.js

  1. import Ember from 'ember';
  2. import { getMousePoint } from '../utils/nf/svg-dom';
  3. import computed from 'ember-new-computed';
  4.  
  5. var { on, observer } = Ember;
  6.  
  7. export default Ember.Mixin.create({
  8. /**
  9. Gets or sets the tracking mode of the component.
  10.  
  11. Possible values are:
  12.  
  13. - 'none': no tracking behavior
  14. - 'hover': only track while mouse hover
  15. - 'snap-last': track while mouse hover, but snap to the last data element when not hovering
  16. - 'snap-first': track while mouse hover, but snap to the first data element when not hovering
  17. - 'selected-hover': The same as `'hover'` tracking mode, but only when the compononent is
  18. {{#crossLink "mixins.graph-selectable-graphic/selected:property"}}{{/crossLink}}
  19. - 'selected-snap-last': The same as `'snap-last'` tracking mode, but only when the compononent is
  20. {{#crossLink "mixins.graph-selectable-graphic/selected:property"}}{{/crossLink}}
  21. - 'selected-snap-first': The same as `'snap-first'` tracking mode, but only when the compononent is
  22. {{#crossLink "mixins.graph-selectable-graphic/selected:property"}}{{/crossLink}}
  23.  
  24. @property trackingMode
  25. @type String
  26. @default 'none'
  27. */
  28. trackingMode: 'none',
  29.  
  30. /**
  31. The radius of the tracking dot in pixels
  32. @property trackingDotRadius
  33. @type {number}
  34. @default 2.5
  35. */
  36. trackingDotRadius: 2.5,
  37.  
  38. /**
  39. The action to send on `didTrack`.
  40. @property didTrack
  41. @type String
  42. @default null
  43. */
  44. didTrack: null,
  45.  
  46. /**
  47. The value of the data that is being tracked by the component.
  48. @property trackedData
  49. @type {Object} an object with the following values:
  50. - point: an { x, y } pair for the exact px coordinates inside the graph-content
  51. - graphX: domain x value at mouse position
  52. - graphY: domain y value at mouse position
  53. - x: nearest x data value
  54. - y: nearest y data value
  55. - data: nearest raw data
  56. - renderX: domain x value to render a tracking dot at (stacked areas are offset)
  57. - renderY: domain x value to render a tracking dot at (stacked areas are offset)
  58. - mouseX: mouse x position in pixels
  59. - mouseY: mouse y position in pixels
  60. @default null
  61. */
  62. trackedData: null,
  63.  
  64. /**
  65. The value of the data that is being tracked by the component, ONLY if the
  66. graph-content is currently being hovered.
  67. @property hoverData
  68. @type {Object} an object with the following values:
  69. - point: an { x, y } pair for the exact px coordinates inside the graph-content
  70. - graphX: domain x value at mouse position
  71. - graphY: domain y value at mouse position
  72. - x: nearest x data value
  73. - y: nearest y data value
  74. - data: nearest raw data
  75. - renderX: domain x value to render a tracking dot at (stacked areas are offset)
  76. - renderY: domain x value to render a tracking dot at (stacked areas are offset)
  77. - mouseX: mouse x position in pixels
  78. - mouseY: mouse y position in pixels
  79. @default null
  80. */
  81. hoverData: null,
  82.  
  83. _showTrackingDot: true,
  84.  
  85. /**
  86. Gets or sets whether the tracking dot should be shown at all.
  87. @property showTrackingDot
  88. @type {boolean}
  89. @default true
  90. */
  91. showTrackingDot: computed('trackedData', {
  92. get() {
  93. return Boolean(this._showTrackingDot && this.get('trackedData'));
  94. },
  95.  
  96. set(value) {
  97. this._showTrackingDot = value;
  98. }
  99. }),
  100.  
  101. /**
  102. Observes changes to tracked data and sends the
  103. didTrack action.
  104. @method _trackedDataChanged
  105. @private
  106. */
  107. _trackedDataChanged: Ember.observer('trackedData', function(){
  108. var trackedData = this.get('trackedData');
  109. this.set('hoverData', this._hovered ? trackedData : null);
  110.  
  111. if(this.get('didTrack')) {
  112. this.sendAction('didTrack', {
  113. x: trackedData.x,
  114. y: trackedData.y,
  115. data: trackedData.data,
  116. source: this,
  117. graph: this.get('graph'),
  118. });
  119. }
  120. }),
  121.  
  122. _cleanup: function(){
  123. if(this._onHoverCleanup) {
  124. this._onHoverCleanup();
  125. }
  126. if(this._onEndCleanup) {
  127. this._onEndCleanup();
  128. }
  129. },
  130.  
  131. _updateTrackingHandling() {
  132. var { trackingMode, selected } = this.getProperties('trackingMode', 'selected');
  133.  
  134. this._cleanup();
  135.  
  136. switch(trackingMode) {
  137. case 'hover':
  138. this._onHoverTrack();
  139. this._onEndUntrack();
  140. break;
  141. case 'snap-first':
  142. this._onHoverTrack();
  143. this._onEndSnapFirst();
  144. break;
  145. case 'snap-last':
  146. this._onHoverTrack();
  147. this._onEndSnapLast();
  148. break;
  149. case 'selected-hover':
  150. if(selected) {
  151. this._onHoverTrack();
  152. this._onEndUntrack();
  153. }
  154. break;
  155. case 'selected-snap-first':
  156. if(selected) {
  157. this._onHoverTrack();
  158. this._onEndSnapFirst();
  159. }
  160. break;
  161. case 'selected-snap-last':
  162. if(selected) {
  163. this._onHoverTrack();
  164. this._onEndSnapLast();
  165. }
  166. break;
  167. }
  168. },
  169.  
  170. _onHoverTrack() {
  171. var content = this._content;
  172.  
  173. var mousemoveHandler = e => {
  174. this._hovered = true;
  175. var evt = this._getEventObject(e);
  176. this.set('trackedData', evt);
  177. };
  178.  
  179. content.on('mousemove', mousemoveHandler);
  180.  
  181. this._onHoverCleanup = () => {
  182. content.off('mousemove', mousemoveHandler);
  183. };
  184. },
  185.  
  186. _hovered: false,
  187.  
  188. _onEndUntrack() {
  189. var content = this._content;
  190.  
  191. var mouseoutHandler = () => {
  192. this.set('trackedData', null);
  193. };
  194.  
  195. content.on('mouseout', mouseoutHandler);
  196.  
  197. this._onEndCleanup = () => {
  198. content.off('mouseout', mouseoutHandler);
  199. };
  200.  
  201. if(!this._hovered) {
  202. this.set('trackedData', null);
  203. }
  204. },
  205.  
  206. _onEndSnapLast() {
  207. var content = this._content;
  208.  
  209. var mouseoutHandler = () => {
  210. this._hovered = false;
  211. this.set('trackedData', this.get('lastVisibleData'));
  212. };
  213.  
  214. var changeHandler = () => {
  215. if(!this._hovered) {
  216. this.set('trackedData', this.get('lastVisibleData'));
  217. }
  218. };
  219.  
  220. content.on('mouseout', mouseoutHandler);
  221. this.addObserver('lastVisibleData', this, changeHandler);
  222.  
  223. this._onEndCleanup = () => {
  224. content.off('mouseout', mouseoutHandler);
  225. this.removeObserver('lastVisibleData', this, changeHandler);
  226. };
  227.  
  228. changeHandler();
  229. },
  230.  
  231. _onEndSnapFirst() {
  232. var content = this._content;
  233.  
  234. var mouseoutHandler = () => {
  235. this._hovered = false;
  236. this.set('trackedData', this.get('firstVisibleData'));
  237. };
  238.  
  239. var changeHandler = () => {
  240. if(!this._hovered) {
  241. this.set('trackedData', this.get('firstVisibleData'));
  242. }
  243. };
  244.  
  245. content.on('mouseout', mouseoutHandler);
  246. this.addObserver('firstVisibleData', this, changeHandler);
  247.  
  248. this._onEndCleanup = () => {
  249. content.off('mouseout', mouseoutHandler);
  250. this.removeObserver('firstVisibleData', this, changeHandler);
  251. };
  252.  
  253. changeHandler();
  254. },
  255.  
  256. _trackingModeChanged: on('init', observer('trackingMode', 'selected', function() {
  257. Ember.run.once(this, this._updateTrackingHandling);
  258. })),
  259.  
  260. _getEventObject(e) {
  261. var { xScale, yScale } = this.getProperties('xScale', 'yScale');
  262. var content = this._content;
  263. var point = getMousePoint(content[0], e);
  264. var graphX = xScale.invert(point.x);
  265. var graphY = yScale.invert(point.y);
  266. var near = this.getDataNearXRange(point.x);
  267.  
  268. if(!near) {
  269. return {
  270. point,
  271. graphX,
  272. graphY,
  273. mouseX: point.x,
  274. mouseY: point.y,
  275. };
  276. }
  277.  
  278. var { x, y, data, renderX, renderY } = near;
  279. return {
  280. point,
  281. graphX,
  282. graphY,
  283. x,
  284. y,
  285. data,
  286. renderX,
  287. renderY,
  288. mouseX: point.x,
  289. mouseY: point.y
  290. };
  291. },
  292.  
  293. didInsertElement() {
  294. this._super.apply(arguments);
  295. this._content = this.$().parents('.nf-graph-content');
  296. },
  297.  
  298. willDestroyElement() {
  299. this._super.apply(arguments);
  300. this._cleanup();
  301. }
  302. });