mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-22 21:40:55 +01:00
Label important data on charts
Summary: Ref T13279. Adds client-side support for rendering function labels on charts, then labels every function as important data. Works okay on mobile, although I'm not planning to target mobile terribly heavily for v0. Test Plan: {F6438860} Reviewers: amckinley Reviewed By: amckinley Subscribers: yelirekim Maniphest Tasks: T13279 Differential Revision: https://secure.phabricator.com/D20500
This commit is contained in:
parent
81456db559
commit
c6052b41a6
5 changed files with 201 additions and 29 deletions
|
@ -141,7 +141,7 @@ return array(
|
|||
'rsrc/css/phui/phui-big-info-view.css' => '362ad37b',
|
||||
'rsrc/css/phui/phui-box.css' => '5ed3b8cb',
|
||||
'rsrc/css/phui/phui-bulk-editor.css' => '374d5e30',
|
||||
'rsrc/css/phui/phui-chart.css' => '7853a69b',
|
||||
'rsrc/css/phui/phui-chart.css' => '10135a9d',
|
||||
'rsrc/css/phui/phui-cms.css' => '8c05c41e',
|
||||
'rsrc/css/phui/phui-comment-form.css' => '68a2d99a',
|
||||
'rsrc/css/phui/phui-comment-panel.css' => 'ec4e31c0',
|
||||
|
@ -389,7 +389,8 @@ return array(
|
|||
'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'c715c123',
|
||||
'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => '6a85bc5a',
|
||||
'rsrc/js/application/drydock/drydock-live-operation-status.js' => '47a0728b',
|
||||
'rsrc/js/application/fact/Chart.js' => 'a3516cea',
|
||||
'rsrc/js/application/fact/Chart.js' => 'b88a227d',
|
||||
'rsrc/js/application/fact/ChartCurtainView.js' => 'd10a3c25',
|
||||
'rsrc/js/application/files/behavior-document-engine.js' => '243d6c22',
|
||||
'rsrc/js/application/files/behavior-icon-composer.js' => '38a6cedb',
|
||||
'rsrc/js/application/files/behavior-launch-icon-composer.js' => 'a17b84f1',
|
||||
|
@ -696,7 +697,8 @@ return array(
|
|||
'javelin-behavior-user-menu' => '60cd9241',
|
||||
'javelin-behavior-view-placeholder' => 'a9942052',
|
||||
'javelin-behavior-workflow' => '9623adc1',
|
||||
'javelin-chart' => 'a3516cea',
|
||||
'javelin-chart' => 'b88a227d',
|
||||
'javelin-chart-curtain-view' => 'd10a3c25',
|
||||
'javelin-color' => '78f811c9',
|
||||
'javelin-cookie' => '05d290ef',
|
||||
'javelin-diffusion-locate-file-source' => '94243d89',
|
||||
|
@ -823,7 +825,7 @@ return array(
|
|||
'phui-calendar-day-css' => '9597d706',
|
||||
'phui-calendar-list-css' => 'ccd7e4e2',
|
||||
'phui-calendar-month-css' => 'cb758c42',
|
||||
'phui-chart-css' => '7853a69b',
|
||||
'phui-chart-css' => '10135a9d',
|
||||
'phui-cms-css' => '8c05c41e',
|
||||
'phui-comment-form-css' => '68a2d99a',
|
||||
'phui-comment-panel-css' => 'ec4e31c0',
|
||||
|
@ -1767,10 +1769,6 @@ return array(
|
|||
'javelin-workflow',
|
||||
'phabricator-draggable-list',
|
||||
),
|
||||
'a3516cea' => array(
|
||||
'phui-chart-css',
|
||||
'd3',
|
||||
),
|
||||
'a4356cde' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
@ -1935,6 +1933,11 @@ return array(
|
|||
'javelin-dom',
|
||||
'phabricator-draggable-list',
|
||||
),
|
||||
'b88a227d' => array(
|
||||
'phui-chart-css',
|
||||
'd3',
|
||||
'javelin-chart-curtain-view',
|
||||
),
|
||||
'b9109f8f' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-uri',
|
||||
|
|
|
@ -94,10 +94,8 @@ final class PhabricatorChartRenderingEngine
|
|||
'div',
|
||||
array(
|
||||
'id' => $chart_node_id,
|
||||
'style' => 'background: #ffffff; '.
|
||||
'height: 480px; ',
|
||||
),
|
||||
'');
|
||||
'class' => 'chart-hardpoint',
|
||||
));
|
||||
|
||||
$data_uri = urisprintf('/fact/chart/%s/draw/', $chart_key);
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
}
|
||||
|
||||
.chart .axis text {
|
||||
font: {$basefont};
|
||||
fill: {$darkgreytext};
|
||||
}
|
||||
|
||||
|
@ -52,3 +53,43 @@
|
|||
border-radius: 8px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.chart-hardpoint {
|
||||
min-height: 480px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.device-desktop .chart-container {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 300px;
|
||||
}
|
||||
|
||||
.device .chart-container {
|
||||
min-height: 480px;
|
||||
}
|
||||
|
||||
.device-desktop .chart-curtain {
|
||||
width: 300px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.chart-function-label-list {
|
||||
background: {$lightbluebackground};
|
||||
border: 1px solid {$lightblueborder};
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.device-desktop .chart-function-label-list {
|
||||
margin-top: 23px;
|
||||
}
|
||||
|
||||
.chart-function-label-list-item .phui-icon-view {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
|
79
webroot/rsrc/js/application/fact/Chart.js
vendored
79
webroot/rsrc/js/application/fact/Chart.js
vendored
|
@ -2,6 +2,7 @@
|
|||
* @provides javelin-chart
|
||||
* @requires phui-chart-css
|
||||
* d3
|
||||
* javelin-chart-curtain-view
|
||||
*/
|
||||
JX.install('Chart', {
|
||||
|
||||
|
@ -14,6 +15,8 @@ JX.install('Chart', {
|
|||
members: {
|
||||
_rootNode: null,
|
||||
_data: null,
|
||||
_chartContainerNode: null,
|
||||
_curtain: null,
|
||||
|
||||
setData: function(blob) {
|
||||
this._data = blob;
|
||||
|
@ -26,23 +29,42 @@ JX.install('Chart', {
|
|||
}
|
||||
|
||||
var hardpoint = this._rootNode;
|
||||
var curtain = this._getCurtain();
|
||||
var container_node = this._getChartContainerNode();
|
||||
|
||||
var content = [
|
||||
container_node,
|
||||
curtain.getNode(),
|
||||
];
|
||||
|
||||
JX.DOM.setContent(hardpoint, content);
|
||||
|
||||
// Remove the old chart (if one exists) before drawing the new chart.
|
||||
JX.DOM.setContent(hardpoint, []);
|
||||
JX.DOM.setContent(container_node, []);
|
||||
|
||||
var viewport = JX.Vector.getDim(hardpoint);
|
||||
var viewport = JX.Vector.getDim(container_node);
|
||||
var config = this._data;
|
||||
|
||||
function css_function(n) {
|
||||
return n + '(' + JX.$A(arguments).slice(1).join(', ') + ')';
|
||||
}
|
||||
|
||||
var padding = {
|
||||
top: 24,
|
||||
left: 48,
|
||||
bottom: 48,
|
||||
right: 32
|
||||
};
|
||||
var padding = {};
|
||||
if (JX.Device.isDesktop()) {
|
||||
padding = {
|
||||
top: 24,
|
||||
left: 48,
|
||||
bottom: 48,
|
||||
right: 12
|
||||
};
|
||||
} else {
|
||||
padding = {
|
||||
top: 12,
|
||||
left: 36,
|
||||
bottom: 24,
|
||||
right: 4
|
||||
};
|
||||
}
|
||||
|
||||
var size = {
|
||||
frameWidth: viewport.x,
|
||||
|
@ -61,20 +83,20 @@ JX.install('Chart', {
|
|||
var xAxis = d3.axisBottom(x);
|
||||
var yAxis = d3.axisLeft(y);
|
||||
|
||||
var svg = d3.select('#' + hardpoint.id).append('svg')
|
||||
var svg = d3.select(container_node).append('svg')
|
||||
.attr('width', size.frameWidth)
|
||||
.attr('height', size.frameHeight)
|
||||
.attr('class', 'chart');
|
||||
|
||||
var g = svg.append('g')
|
||||
.attr(
|
||||
'transform',
|
||||
css_function('translate', padding.left, padding.top));
|
||||
.attr(
|
||||
'transform',
|
||||
css_function('translate', padding.left, padding.top));
|
||||
|
||||
g.append('rect')
|
||||
.attr('class', 'inner')
|
||||
.attr('width', size.width)
|
||||
.attr('height', size.height);
|
||||
.attr('class', 'inner')
|
||||
.attr('width', size.width)
|
||||
.attr('height', size.height);
|
||||
|
||||
x.domain([this._newDate(config.xMin), this._newDate(config.xMax)]);
|
||||
y.domain([config.yMin, config.yMax]);
|
||||
|
@ -84,16 +106,20 @@ JX.install('Chart', {
|
|||
.attr('class', 'chart-tooltip')
|
||||
.style('opacity', 0);
|
||||
|
||||
curtain.reset();
|
||||
|
||||
for (var idx = 0; idx < config.datasets.length; idx++) {
|
||||
var dataset = config.datasets[idx];
|
||||
|
||||
switch (dataset.type) {
|
||||
case 'stacked-area':
|
||||
this._newStackedArea(g, dataset, x, y, div);
|
||||
this._newStackedArea(g, dataset, x, y, div, curtain);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
curtain.redraw();
|
||||
|
||||
g.append('g')
|
||||
.attr('class', 'x axis')
|
||||
.attr('transform', css_function('translate', 0, size.height))
|
||||
|
@ -105,7 +131,7 @@ JX.install('Chart', {
|
|||
.call(yAxis);
|
||||
},
|
||||
|
||||
_newStackedArea: function(g, dataset, x, y, div) {
|
||||
_newStackedArea: function(g, dataset, x, y, div, curtain) {
|
||||
var to_date = JX.bind(this, this._newDate);
|
||||
|
||||
var area = d3.area()
|
||||
|
@ -155,11 +181,30 @@ JX.install('Chart', {
|
|||
div.style('opacity', 0);
|
||||
});
|
||||
|
||||
curtain.addFunctionLabel('Important Data');
|
||||
}
|
||||
},
|
||||
|
||||
_newDate: function(epoch) {
|
||||
return new Date(epoch * 1000);
|
||||
},
|
||||
|
||||
_getCurtain: function() {
|
||||
if (!this._curtain) {
|
||||
this._curtain = new JX.ChartCurtainView();
|
||||
}
|
||||
return this._curtain;
|
||||
},
|
||||
|
||||
_getChartContainerNode: function() {
|
||||
if (!this._chartContainerNode) {
|
||||
var attrs = {
|
||||
className: 'chart-container'
|
||||
};
|
||||
|
||||
this._chartContainerNode = JX.$N('div', attrs);
|
||||
}
|
||||
return this._chartContainerNode;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
85
webroot/rsrc/js/application/fact/ChartCurtainView.js
Normal file
85
webroot/rsrc/js/application/fact/ChartCurtainView.js
Normal file
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* @provides javelin-chart-curtain-view
|
||||
*/
|
||||
JX.install('ChartCurtainView', {
|
||||
|
||||
construct: function() {
|
||||
this._labels = [];
|
||||
},
|
||||
|
||||
members: {
|
||||
_node: null,
|
||||
_labels: null,
|
||||
_labelsNode: null,
|
||||
|
||||
getNode: function() {
|
||||
if (!this._node) {
|
||||
var attr = {
|
||||
className: 'chart-curtain'
|
||||
};
|
||||
|
||||
this._node = JX.$N('div', attr);
|
||||
}
|
||||
return this._node;
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this._labels = [];
|
||||
},
|
||||
|
||||
addFunctionLabel: function(label) {
|
||||
this._labels.push(label);
|
||||
return this;
|
||||
},
|
||||
|
||||
redraw: function() {
|
||||
var content = [this._getFunctionLabelsNode()];
|
||||
|
||||
JX.DOM.setContent(this.getNode(), content);
|
||||
return this;
|
||||
},
|
||||
|
||||
_getFunctionLabelsNode: function() {
|
||||
if (!this._labels.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!this._labelsNode) {
|
||||
var list_attrs = {
|
||||
className: 'chart-function-label-list'
|
||||
};
|
||||
|
||||
var labels = JX.$N('ul', list_attrs);
|
||||
|
||||
var items = [];
|
||||
for (var ii = 0; ii < this._labels.length; ii++) {
|
||||
items.push(this._newFunctionLabelItem(this._labels[ii]));
|
||||
}
|
||||
|
||||
JX.DOM.setContent(labels, items);
|
||||
|
||||
this._labelsNode = labels;
|
||||
}
|
||||
|
||||
return this._labelsNode;
|
||||
},
|
||||
|
||||
_newFunctionLabelItem: function(item) {
|
||||
var item_attrs = {
|
||||
className: 'chart-function-label-list-item'
|
||||
};
|
||||
|
||||
var icon = new JX.PHUIXIconView()
|
||||
.setIcon('fa-circle');
|
||||
|
||||
var content = [
|
||||
icon.getNode(),
|
||||
item
|
||||
];
|
||||
|
||||
return JX.$N('li', item_attrs, content);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
Loading…
Reference in a new issue