Files
phabricator/webroot/rsrc/js/application/diff/ScrollObjectiveList.js
epriestley e15009b76e Show "reply" inlines as replies in the objective list
Summary: Ref T12733. Show a "reply" icon for replies, and make them stack directly under their parent.

Test Plan: {F4968500}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T12733

Differential Revision: https://secure.phabricator.com/D17981
2017-05-20 08:00:50 -07:00

151 lines
3.4 KiB
JavaScript

/**
* @provides phabricator-scroll-objective-list
* @requires javelin-dom
* javelin-util
* javelin-stratcom
* javelin-install
* javelin-workflow
* javelin-scrollbar
* phabricator-scroll-objective
* @javelin
*/
JX.install('ScrollObjectiveList', {
construct : function() {
this._objectives = [];
var onresize = JX.bind(this, this._dirty);
JX.Stratcom.listen('resize', null, onresize);
},
members: {
_objectives: null,
_visible: false,
_trigger: null,
newObjective: function() {
var objective = new JX.ScrollObjective()
.setObjectiveList(this);
this._objectives.push(objective);
this._getNode().appendChild(objective.getNode());
this._dirty();
return objective;
},
show: function() {
this._visible = true;
this._dirty();
return this;
},
hide: function() {
this._visible = false;
this._dirty();
return this;
},
_getNode: function() {
if (!this._node) {
var node = new JX.$N('div', {className: 'scroll-objective-list'});
this._node = node;
}
return this._node;
},
_dirty: function() {
if (this._trigger !== null) {
return;
}
this._trigger = setTimeout(JX.bind(this, this._redraw), 0);
},
_redraw: function() {
this._trigger = null;
var node = this._getNode();
var is_visible =
(this._visible) &&
(JX.Device.getDevice() == 'desktop') &&
(this._objectives.length);
if (!is_visible) {
JX.DOM.remove(node);
return;
}
document.body.appendChild(node);
// If we're on OSX without a mouse or some other system with zero-width
// trackpad-style scrollbars, adjust the display appropriately.
var aesthetic = (JX.Scrollbar.getScrollbarControlWidth() === 0);
JX.DOM.alterClass(node, 'has-aesthetic-scrollbar', aesthetic);
var d = JX.Vector.getDocument();
var list_dimensions = JX.Vector.getDim(node);
var icon_height = 16;
var list_y = (list_dimensions.y - icon_height);
var ii;
var offset;
// First, build a list of all the items we're going to show.
var items = [];
for (ii = 0; ii < this._objectives.length; ii++) {
var objective = this._objectives[ii];
var objective_node = objective.getNode();
var anchor = objective.getAnchor();
if (!anchor || !objective.isVisible()) {
JX.DOM.remove(objective_node);
continue;
}
offset = (JX.$V(anchor).y / d.y) * (list_y);
items.push({
offset: offset,
node: objective_node,
objective: objective
});
}
// Now, sort it from top to bottom.
items.sort(function(u, v) {
return u.offset - v.offset;
});
// Lay out the items in the objective list, leaving a minimum amount
// of space between them so they do not overlap.
var min = null;
for (ii = 0; ii < items.length; ii++) {
var item = items[ii];
offset = item.offset;
if (min !== null) {
if (item.objective.shouldStack()) {
offset = min;
} else {
offset = Math.max(offset, min);
}
}
min = offset + 15;
item.node.style.top = offset + 'px';
node.appendChild(item.node);
}
}
}
});