Support drawing multiple functions on the same chart
Summary: Depends on D20438. Ref T13279. Widgets produced vs widgets sold, etc.
Test Plan: {F6381609}
Reviewers: amckinley
Reviewed By: amckinley
Subscribers: yelirekim
Maniphest Tasks: T13279
Differential Revision: https://secure.phabricator.com/D20439
			
			
This commit is contained in:
		| @@ -397,7 +397,7 @@ return array( | |||||||
|     'rsrc/js/application/herald/PathTypeahead.js' => 'ad486db3', |     'rsrc/js/application/herald/PathTypeahead.js' => 'ad486db3', | ||||||
|     'rsrc/js/application/herald/herald-rule-editor.js' => '0922e81d', |     'rsrc/js/application/herald/herald-rule-editor.js' => '0922e81d', | ||||||
|     'rsrc/js/application/maniphest/behavior-batch-selector.js' => '139ef688', |     'rsrc/js/application/maniphest/behavior-batch-selector.js' => '139ef688', | ||||||
|     'rsrc/js/application/maniphest/behavior-line-chart.js' => '11167911', |     'rsrc/js/application/maniphest/behavior-line-chart.js' => '495cf14d', | ||||||
|     'rsrc/js/application/maniphest/behavior-list-edit.js' => 'c687e867', |     'rsrc/js/application/maniphest/behavior-list-edit.js' => 'c687e867', | ||||||
|     'rsrc/js/application/owners/OwnersPathEditor.js' => '2a8b62d9', |     'rsrc/js/application/owners/OwnersPathEditor.js' => '2a8b62d9', | ||||||
|     'rsrc/js/application/owners/owners-path-editor.js' => 'ff688a7a', |     'rsrc/js/application/owners/owners-path-editor.js' => 'ff688a7a', | ||||||
| @@ -625,7 +625,7 @@ return array( | |||||||
|     'javelin-behavior-icon-composer' => '38a6cedb', |     'javelin-behavior-icon-composer' => '38a6cedb', | ||||||
|     'javelin-behavior-launch-icon-composer' => 'a17b84f1', |     'javelin-behavior-launch-icon-composer' => 'a17b84f1', | ||||||
|     'javelin-behavior-lightbox-attachments' => 'c7e748bf', |     'javelin-behavior-lightbox-attachments' => 'c7e748bf', | ||||||
|     'javelin-behavior-line-chart' => '11167911', |     'javelin-behavior-line-chart' => '495cf14d', | ||||||
|     'javelin-behavior-linked-container' => '74446546', |     'javelin-behavior-linked-container' => '74446546', | ||||||
|     'javelin-behavior-maniphest-batch-selector' => '139ef688', |     'javelin-behavior-maniphest-batch-selector' => '139ef688', | ||||||
|     'javelin-behavior-maniphest-list-editor' => 'c687e867', |     'javelin-behavior-maniphest-list-editor' => 'c687e867', | ||||||
| @@ -1007,12 +1007,6 @@ return array( | |||||||
|       'javelin-workflow', |       'javelin-workflow', | ||||||
|       'phuix-icon-view', |       'phuix-icon-view', | ||||||
|     ), |     ), | ||||||
|     11167911 => array( |  | ||||||
|       'javelin-behavior', |  | ||||||
|       'javelin-dom', |  | ||||||
|       'javelin-vector', |  | ||||||
|       'phui-chart-css', |  | ||||||
|     ), |  | ||||||
|     '111bfd2d' => array( |     '111bfd2d' => array( | ||||||
|       'javelin-install', |       'javelin-install', | ||||||
|     ), |     ), | ||||||
| @@ -1325,6 +1319,12 @@ return array( | |||||||
|     '490e2e2e' => array( |     '490e2e2e' => array( | ||||||
|       'phui-oi-list-view-css', |       'phui-oi-list-view-css', | ||||||
|     ), |     ), | ||||||
|  |     '495cf14d' => array( | ||||||
|  |       'javelin-behavior', | ||||||
|  |       'javelin-dom', | ||||||
|  |       'javelin-vector', | ||||||
|  |       'phui-chart-css', | ||||||
|  |     ), | ||||||
|     '4a7fb02b' => array( |     '4a7fb02b' => array( | ||||||
|       'javelin-behavior', |       'javelin-behavior', | ||||||
|       'javelin-dom', |       'javelin-dom', | ||||||
|   | |||||||
| @@ -55,8 +55,29 @@ final class PhabricatorFactChartController extends PhabricatorFactController { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $x = array_keys($points); |     $datasets = array(); | ||||||
|     $y = array_values($points); |  | ||||||
|  |     $datasets[] = array( | ||||||
|  |       'x' => array_keys($points), | ||||||
|  |       'y' => array_values($points), | ||||||
|  |       'color' => '#ff0000', | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     // Add a dummy "y = x" dataset to prove we can draw multiple datasets. | ||||||
|  |     $x_min = min(array_keys($points)); | ||||||
|  |     $x_max = max(array_keys($points)); | ||||||
|  |     $x_range = ($x_max - $x_min) / 4; | ||||||
|  |     $linear = array(); | ||||||
|  |     foreach ($points as $x => $y) { | ||||||
|  |       $linear[$x] = count($points) * (($x - $x_min) / $x_range); | ||||||
|  |     } | ||||||
|  |     $datasets[] = array( | ||||||
|  |       'x' => array_keys($linear), | ||||||
|  |       'y' => array_values($linear), | ||||||
|  |       'color' => '#0000ff', | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |  | ||||||
|     $id = celerity_generate_unique_node_id(); |     $id = celerity_generate_unique_node_id(); | ||||||
|     $chart = phutil_tag( |     $chart = phutil_tag( | ||||||
| @@ -70,15 +91,38 @@ final class PhabricatorFactChartController extends PhabricatorFactController { | |||||||
|  |  | ||||||
|     require_celerity_resource('d3'); |     require_celerity_resource('d3'); | ||||||
|  |  | ||||||
|     Javelin::initBehavior('line-chart', array( |     $y_min = 0; | ||||||
|       'hardpoint' => $id, |     $y_max = 0; | ||||||
|       'x' => array($x), |     $x_min = null; | ||||||
|       'y' => array($y), |     $x_max = 0; | ||||||
|       'yMax' => max(0, max($y)), |     foreach ($datasets as $dataset) { | ||||||
|       'yMin' => min(0, min($y)), |       if (!$dataset['y']) { | ||||||
|       'xformat' => 'epoch', |         continue; | ||||||
|       'colors' => array('#0000ff'), |       } | ||||||
|     )); |  | ||||||
|  |       $y_min = min($y_min, min($dataset['y'])); | ||||||
|  |       $y_max = max($y_max, max($dataset['y'])); | ||||||
|  |  | ||||||
|  |       if ($x_min === null) { | ||||||
|  |         $x_min = min($dataset['x']); | ||||||
|  |       } else { | ||||||
|  |         $x_min = min($x_min, min($dataset['x'])); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $x_max = max($x_max, max($dataset['x'])); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Javelin::initBehavior( | ||||||
|  |       'line-chart', | ||||||
|  |       array( | ||||||
|  |         'hardpoint' => $id, | ||||||
|  |         'datasets' => $datasets, | ||||||
|  |         'xMin' => $x_min, | ||||||
|  |         'xMax' => $x_max, | ||||||
|  |         'yMin' => $y_min, | ||||||
|  |         'yMax' => $y_max, | ||||||
|  |         'xformat' => 'epoch', | ||||||
|  |       )); | ||||||
|  |  | ||||||
|     $box = id(new PHUIObjectBoxView()) |     $box = id(new PHUIObjectBoxView()) | ||||||
|       ->setHeaderText(pht('Count of %s', $fact->getName())) |       ->setHeaderText(pht('Count of %s', $fact->getName())) | ||||||
|   | |||||||
| @@ -57,28 +57,61 @@ JX.behavior('line-chart', function(config) { | |||||||
|       .attr('width', size.width) |       .attr('width', size.width) | ||||||
|       .attr('height', size.height); |       .attr('height', size.height); | ||||||
|  |  | ||||||
|   var line = d3.svg.line() |   function as_date(value) { | ||||||
|     .x(function(d) { return x(d.date); }) |     return new Date(value * 1000); | ||||||
|     .y(function(d) { return y(d.count); }); |  | ||||||
|  |  | ||||||
|   var data = []; |  | ||||||
|   for (var ii = 0; ii < config.x[0].length; ii++) { |  | ||||||
|     data.push( |  | ||||||
|       { |  | ||||||
|         date: new Date(config.x[0][ii] * 1000), |  | ||||||
|         count: +config.y[0][ii] |  | ||||||
|       }); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   x.domain(d3.extent(data, function(d) { return d.date; })); |   x.domain([as_date(config.xMin), as_date(config.xMax)]); | ||||||
|  |  | ||||||
|   var yex = d3.extent(data, function(d) { return d.count; }); |  | ||||||
|   y.domain([config.yMin, config.yMax]); |   y.domain([config.yMin, config.yMax]); | ||||||
|  |  | ||||||
|   g.append('path') |   for (var idx = 0; idx < config.datasets.length; idx++) { | ||||||
|     .datum(data) |     var dataset = config.datasets[idx]; | ||||||
|     .attr('class', 'line') |  | ||||||
|     .attr('d', line); |     var line = d3.svg.line() | ||||||
|  |       .x(function(d) { return x(d.xvalue); }) | ||||||
|  |       .y(function(d) { return y(d.yvalue); }); | ||||||
|  |  | ||||||
|  |     var data = []; | ||||||
|  |     for (var ii = 0; ii < dataset.x.length; ii++) { | ||||||
|  |       data.push( | ||||||
|  |         { | ||||||
|  |           xvalue: as_date(dataset.x[ii]), | ||||||
|  |           yvalue: dataset.y[ii] | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     g.append('path') | ||||||
|  |       .datum(data) | ||||||
|  |       .attr('class', 'line') | ||||||
|  |       .style('stroke', dataset.color) | ||||||
|  |       .attr('d', line); | ||||||
|  |  | ||||||
|  |     g.selectAll('dot') | ||||||
|  |       .data(data) | ||||||
|  |       .enter() | ||||||
|  |       .append('circle') | ||||||
|  |       .attr('class', 'point') | ||||||
|  |       .attr('r', 3) | ||||||
|  |       .attr('cx', function(d) { return x(d.xvalue); }) | ||||||
|  |       .attr('cy', function(d) { return y(d.yvalue); }) | ||||||
|  |       .on('mouseover', function(d) { | ||||||
|  |         var d_y = d.xvalue.getFullYear(); | ||||||
|  |  | ||||||
|  |         // NOTE: Javascript months are zero-based. See PHI1017. | ||||||
|  |         var d_m = d.xvalue.getMonth() + 1; | ||||||
|  |  | ||||||
|  |         var d_d = d.xvalue.getDate(); | ||||||
|  |  | ||||||
|  |         div | ||||||
|  |           .html(d_y + '-' + d_m + '-' + d_d + ': ' + d.yvalue) | ||||||
|  |           .style('opacity', 0.9) | ||||||
|  |           .style('left', (d3.event.pageX - 60) + 'px') | ||||||
|  |           .style('top', (d3.event.pageY - 38) + 'px'); | ||||||
|  |         }) | ||||||
|  |       .on('mouseout', function() { | ||||||
|  |         div.style('opacity', 0); | ||||||
|  |       }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   g.append('g') |   g.append('g') | ||||||
|     .attr('class', 'x axis') |     .attr('class', 'x axis') | ||||||
| @@ -95,30 +128,4 @@ JX.behavior('line-chart', function(config) { | |||||||
|     .attr('class', 'chart-tooltip') |     .attr('class', 'chart-tooltip') | ||||||
|     .style('opacity', 0); |     .style('opacity', 0); | ||||||
|  |  | ||||||
|   g.selectAll('dot') |  | ||||||
|     .data(data) |  | ||||||
|     .enter() |  | ||||||
|     .append('circle') |  | ||||||
|     .attr('class', 'point') |  | ||||||
|     .attr('r', 3) |  | ||||||
|     .attr('cx', function(d) { return x(d.date); }) |  | ||||||
|     .attr('cy', function(d) { return y(d.count); }) |  | ||||||
|     .on('mouseover', function(d) { |  | ||||||
|       var d_y = d.date.getFullYear(); |  | ||||||
|  |  | ||||||
|       // NOTE: Javascript months are zero-based. See PHI1017. |  | ||||||
|       var d_m = d.date.getMonth() + 1; |  | ||||||
|  |  | ||||||
|       var d_d = d.date.getDate(); |  | ||||||
|  |  | ||||||
|       div |  | ||||||
|         .html(d_y + '-' + d_m + '-' + d_d + ': ' + d.count) |  | ||||||
|         .style('opacity', 0.9) |  | ||||||
|         .style('left', (d3.event.pageX - 60) + 'px') |  | ||||||
|         .style('top', (d3.event.pageY - 38) + 'px'); |  | ||||||
|       }) |  | ||||||
|     .on('mouseout', function() { |  | ||||||
|       div.style('opacity', 0); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
| }); | }); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 epriestley
					epriestley