Page MenuHomePhabricator

D20439.id48794.diff
No OneTemporary

D20439.id48794.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -397,7 +397,7 @@
'rsrc/js/application/herald/PathTypeahead.js' => 'ad486db3',
'rsrc/js/application/herald/herald-rule-editor.js' => '0922e81d',
'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/owners/OwnersPathEditor.js' => '2a8b62d9',
'rsrc/js/application/owners/owners-path-editor.js' => 'ff688a7a',
@@ -625,7 +625,7 @@
'javelin-behavior-icon-composer' => '38a6cedb',
'javelin-behavior-launch-icon-composer' => 'a17b84f1',
'javelin-behavior-lightbox-attachments' => 'c7e748bf',
- 'javelin-behavior-line-chart' => '11167911',
+ 'javelin-behavior-line-chart' => '495cf14d',
'javelin-behavior-linked-container' => '74446546',
'javelin-behavior-maniphest-batch-selector' => '139ef688',
'javelin-behavior-maniphest-list-editor' => 'c687e867',
@@ -1007,12 +1007,6 @@
'javelin-workflow',
'phuix-icon-view',
),
- 11167911 => array(
- 'javelin-behavior',
- 'javelin-dom',
- 'javelin-vector',
- 'phui-chart-css',
- ),
'111bfd2d' => array(
'javelin-install',
),
@@ -1325,6 +1319,12 @@
'490e2e2e' => array(
'phui-oi-list-view-css',
),
+ '495cf14d' => array(
+ 'javelin-behavior',
+ 'javelin-dom',
+ 'javelin-vector',
+ 'phui-chart-css',
+ ),
'4a7fb02b' => array(
'javelin-behavior',
'javelin-dom',
diff --git a/src/applications/fact/controller/PhabricatorFactChartController.php b/src/applications/fact/controller/PhabricatorFactChartController.php
--- a/src/applications/fact/controller/PhabricatorFactChartController.php
+++ b/src/applications/fact/controller/PhabricatorFactChartController.php
@@ -55,8 +55,29 @@
}
}
- $x = array_keys($points);
- $y = array_values($points);
+ $datasets = array();
+
+ $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();
$chart = phutil_tag(
@@ -70,15 +91,38 @@
require_celerity_resource('d3');
- Javelin::initBehavior('line-chart', array(
- 'hardpoint' => $id,
- 'x' => array($x),
- 'y' => array($y),
- 'yMax' => max(0, max($y)),
- 'yMin' => min(0, min($y)),
- 'xformat' => 'epoch',
- 'colors' => array('#0000ff'),
- ));
+ $y_min = 0;
+ $y_max = 0;
+ $x_min = null;
+ $x_max = 0;
+ foreach ($datasets as $dataset) {
+ if (!$dataset['y']) {
+ continue;
+ }
+
+ $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())
->setHeaderText(pht('Count of %s', $fact->getName()))
diff --git a/webroot/rsrc/js/application/maniphest/behavior-line-chart.js b/webroot/rsrc/js/application/maniphest/behavior-line-chart.js
--- a/webroot/rsrc/js/application/maniphest/behavior-line-chart.js
+++ b/webroot/rsrc/js/application/maniphest/behavior-line-chart.js
@@ -57,28 +57,61 @@
.attr('width', size.width)
.attr('height', size.height);
- var line = d3.svg.line()
- .x(function(d) { return x(d.date); })
- .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]
- });
+ function as_date(value) {
+ return new Date(value * 1000);
}
- x.domain(d3.extent(data, function(d) { return d.date; }));
-
- var yex = d3.extent(data, function(d) { return d.count; });
+ x.domain([as_date(config.xMin), as_date(config.xMax)]);
y.domain([config.yMin, config.yMax]);
- g.append('path')
- .datum(data)
- .attr('class', 'line')
- .attr('d', line);
+ for (var idx = 0; idx < config.datasets.length; idx++) {
+ var dataset = config.datasets[idx];
+
+ 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')
.attr('class', 'x axis')
@@ -95,30 +128,4 @@
.attr('class', 'chart-tooltip')
.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);
- });
-
});

File Metadata

Mime Type
text/plain
Expires
Mon, May 20, 10:21 AM (3 w, 5 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6301528
Default Alt Text
D20439.id48794.diff (7 KB)

Event Timeline