mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-30 02:32:42 +01:00
Make scrolling happen relative to the main content frame
Summary: Fixes T7033. When we've reframed the main page content we need to scroll relative to the containing frame, not relative to the window. Test Plan: In Safari, Chrome and Firefox, used j/k/J/K keys to navigate diff content. Tried some other scroll-based beahviors, like jump-to-anchors. (It looks like the highlighting reticle got slightly derped a while ago, but it's still functional, so I didn't mess with it.) Reviewers: btrahan, chad Reviewed By: chad Subscribers: epriestley Maniphest Tasks: T7033 Differential Revision: https://secure.phabricator.com/D11490
This commit is contained in:
parent
cd73f45c7e
commit
ea67a8ab8e
10 changed files with 100 additions and 12 deletions
|
@ -344,6 +344,7 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView {
|
||||||
'scrollbar',
|
'scrollbar',
|
||||||
array(
|
array(
|
||||||
'nodeID' => 'phabricator-standard-page',
|
'nodeID' => 'phabricator-standard-page',
|
||||||
|
'isMainContent' => true,
|
||||||
));
|
));
|
||||||
|
|
||||||
$main_page = phutil_tag(
|
$main_page = phutil_tag(
|
||||||
|
|
61
webroot/rsrc/externals/javelin/lib/DOM.js
vendored
61
webroot/rsrc/externals/javelin/lib/DOM.js
vendored
|
@ -336,6 +336,8 @@ JX.install('DOM', {
|
||||||
_autoid : 0,
|
_autoid : 0,
|
||||||
_uniqid : 0,
|
_uniqid : 0,
|
||||||
_metrics : {},
|
_metrics : {},
|
||||||
|
_frameNode: null,
|
||||||
|
_contentNode: null,
|
||||||
|
|
||||||
|
|
||||||
/* -( Changing DOM Content )----------------------------------------------- */
|
/* -( Changing DOM Content )----------------------------------------------- */
|
||||||
|
@ -935,14 +937,71 @@ JX.install('DOM', {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set specific nodes as content and frame nodes for the document.
|
||||||
|
*
|
||||||
|
* This will cause @{method:scrollTo} and @{method:scrollToPosition} to
|
||||||
|
* affect the given frame node instead of the window. This is useful if the
|
||||||
|
* page content is broken into multiple panels which scroll independently.
|
||||||
|
*
|
||||||
|
* Normally, both nodes are the document body.
|
||||||
|
*
|
||||||
|
* @task view
|
||||||
|
* @param Node Node to set as the scroll frame.
|
||||||
|
* @param Node Node to set as the content frame.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
setContentFrame: function(frame_node, content_node) {
|
||||||
|
JX.DOM._frameNode = frame_node;
|
||||||
|
JX.DOM._contentNode = content_node;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current content frame, or `document.body` if one has not been
|
||||||
|
* set.
|
||||||
|
*
|
||||||
|
* @task view
|
||||||
|
* @return Node The node which frames the main page content.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
getContentFrame: function() {
|
||||||
|
return JX.DOM._contentNode || document.body;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scroll to the position of an element in the document.
|
* Scroll to the position of an element in the document.
|
||||||
|
*
|
||||||
|
* If @{method:setContentFrame} has been used to set a frame, that node is
|
||||||
|
* scrolled.
|
||||||
|
*
|
||||||
* @task view
|
* @task view
|
||||||
* @param Node Node to move document scroll position to, if possible.
|
* @param Node Node to move document scroll position to, if possible.
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
scrollTo : function(node) {
|
scrollTo : function(node) {
|
||||||
window.scrollTo(0, JX.$V(node).y);
|
JX.DOM._scrollToPosition(0, JX.$V(node).y);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll to a specific position in the document.
|
||||||
|
*
|
||||||
|
* If @{method:setContentFrame} has been used to set a frame, that node is
|
||||||
|
* scrolled.
|
||||||
|
*
|
||||||
|
* @task view
|
||||||
|
* @param int X position, in pixels.
|
||||||
|
* @param int Y position, in pixels.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
scrollToPosition: function(x, y) {
|
||||||
|
var self = JX.DOM;
|
||||||
|
if (self._frameNode) {
|
||||||
|
self._frameNode.scrollLeft = x;
|
||||||
|
self._frameNode.scrollTop = y;
|
||||||
|
} else {
|
||||||
|
window.scrollTo(x, y);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_getAutoID : function(node) {
|
_getAutoID : function(node) {
|
||||||
|
|
23
webroot/rsrc/externals/javelin/lib/Scrollbar.js
vendored
23
webroot/rsrc/externals/javelin/lib/Scrollbar.js
vendored
|
@ -25,6 +25,8 @@
|
||||||
JX.install('Scrollbar', {
|
JX.install('Scrollbar', {
|
||||||
|
|
||||||
construct: function(frame) {
|
construct: function(frame) {
|
||||||
|
this._frame = frame;
|
||||||
|
|
||||||
// Before doing anything, check if the scrollbar control has a measurable
|
// Before doing anything, check if the scrollbar control has a measurable
|
||||||
// width. If it doesn't, we're already in an environment with an aesthetic
|
// width. If it doesn't, we're already in an environment with an aesthetic
|
||||||
// scrollbar (like Safari on OSX with no mouse connected, or an iPhone)
|
// scrollbar (like Safari on OSX with no mouse connected, or an iPhone)
|
||||||
|
@ -60,7 +62,6 @@ JX.install('Scrollbar', {
|
||||||
var viewport = JX.$N('div', {className: 'jx-scrollbar-viewport'}, content);
|
var viewport = JX.$N('div', {className: 'jx-scrollbar-viewport'}, content);
|
||||||
JX.DOM.appendContent(frame, viewport);
|
JX.DOM.appendContent(frame, viewport);
|
||||||
|
|
||||||
this._frame = frame;
|
|
||||||
this._viewport = viewport;
|
this._viewport = viewport;
|
||||||
this._content = content;
|
this._content = content;
|
||||||
|
|
||||||
|
@ -134,6 +135,26 @@ JX.install('Scrollbar', {
|
||||||
_scrollOrigin: null,
|
_scrollOrigin: null,
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark this content as the scroll frame.
|
||||||
|
*
|
||||||
|
* This changes the behavior of the @{class:JX.DOM} scroll functions so the
|
||||||
|
* continue to work properly if the main page content is reframed to scroll
|
||||||
|
* independently.
|
||||||
|
*/
|
||||||
|
setAsScrollFrame: function() {
|
||||||
|
if (this._viewport) {
|
||||||
|
// If we activated the scrollbar, the viewport and content nodes become
|
||||||
|
// the new scroll and content frames.
|
||||||
|
JX.DOM.setContentFrame(this._viewport, this._content);
|
||||||
|
} else {
|
||||||
|
// Otherwise, the unaltered content frame is both the scroll frame and
|
||||||
|
// content frame.
|
||||||
|
JX.DOM.setContentFrame(this._frame, this._frame);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* After the user scrolls the page, show the scrollbar to give them
|
* After the user scrolls the page, show the scrollbar to give them
|
||||||
* feedback about their position.
|
* feedback about their position.
|
||||||
|
|
|
@ -204,7 +204,7 @@ JX.install('Workflow', {
|
||||||
// The `focus()` call may have scrolled the window. Scroll it back to
|
// The `focus()` call may have scrolled the window. Scroll it back to
|
||||||
// where it was before -- we want to focus the control, but not adjust
|
// where it was before -- we want to focus the control, but not adjust
|
||||||
// the scroll position.
|
// the scroll position.
|
||||||
window.scrollTo(s.x, s.y);
|
JX.DOM.scrollToPosition(s.x, s.y);
|
||||||
|
|
||||||
} else if (this.getHandler()) {
|
} else if (this.getHandler()) {
|
||||||
this.getHandler()(r);
|
this.getHandler()(r);
|
||||||
|
|
|
@ -257,7 +257,7 @@ JX.install('ChangesetViewManager', {
|
||||||
if (near_bot || above_mid) {
|
if (near_bot || above_mid) {
|
||||||
// Figure out how much taller the document got.
|
// Figure out how much taller the document got.
|
||||||
var delta = (JX.Vector.getDocument().y - old_dim.y);
|
var delta = (JX.Vector.getDocument().y - old_dim.y);
|
||||||
window.scrollTo(old_pos.x, old_pos.y + delta);
|
JX.DOM.scrollToPosition(old_pos.x, old_pos.y + delta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._stabilize = false;
|
this._stabilize = false;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
JX.behavior('diffusion-jump-to', function(config) {
|
JX.behavior('diffusion-jump-to', function(config) {
|
||||||
|
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
window.scrollTo(0, JX.$V(JX.$(config.target)).y - 100);
|
JX.DOM.scrollTo(0, JX.$V(JX.$(config.target)).y - 100);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,7 +22,7 @@ JX.behavior('releeph-request-state-change', function() {
|
||||||
|
|
||||||
if (keynav_cursor < 0) {
|
if (keynav_cursor < 0) {
|
||||||
keynav_cursor = -1;
|
keynav_cursor = -1;
|
||||||
window.scrollTo(0);
|
JX.DOM.scrollToPosition(0, 0);
|
||||||
keynavMarkup();
|
keynavMarkup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,9 @@ JX.install('KeyboardShortcutManager', {
|
||||||
* Scroll an element into view.
|
* Scroll an element into view.
|
||||||
*/
|
*/
|
||||||
scrollTo : function(node) {
|
scrollTo : function(node) {
|
||||||
window.scrollTo(0, JX.$V(node).y - 60);
|
var scroll_distance = JX.Vector.getAggregateScrollForNode(node);
|
||||||
|
var node_position = JX.$V(node);
|
||||||
|
JX.DOM.scrollToPosition(0, node_position.y + scroll_distance.y - 60);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -92,7 +94,9 @@ JX.install('KeyboardShortcutManager', {
|
||||||
// Outset the reticle some pixels away from the element, so there's some
|
// Outset the reticle some pixels away from the element, so there's some
|
||||||
// space between the focused element and the outline.
|
// space between the focused element and the outline.
|
||||||
var p = JX.Vector.getPos(node);
|
var p = JX.Vector.getPos(node);
|
||||||
p.add(-4, -4).setPos(r);
|
var s = JX.Vector.getAggregateScrollForNode(node);
|
||||||
|
|
||||||
|
p.add(s).add(-4, -4).setPos(r);
|
||||||
// Compute the size we need to extend to the full extent of the focused
|
// Compute the size we need to extend to the full extent of the focused
|
||||||
// nodes.
|
// nodes.
|
||||||
JX.Vector.getPos(extended_node)
|
JX.Vector.getPos(extended_node)
|
||||||
|
@ -100,7 +104,7 @@ JX.install('KeyboardShortcutManager', {
|
||||||
.add(JX.Vector.getDim(extended_node))
|
.add(JX.Vector.getDim(extended_node))
|
||||||
.add(8, 8)
|
.add(8, 8)
|
||||||
.setDim(r);
|
.setDim(r);
|
||||||
document.body.appendChild(r);
|
JX.DOM.getContentFrame().appendChild(r);
|
||||||
|
|
||||||
this._focusReticle = r;
|
this._focusReticle = r;
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,5 +5,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
JX.behavior('scrollbar', function(config) {
|
JX.behavior('scrollbar', function(config) {
|
||||||
new JX.Scrollbar(JX.$(config.nodeID));
|
var bar = new JX.Scrollbar(JX.$(config.nodeID));
|
||||||
|
if (config.isMainContent) {
|
||||||
|
bar.setAsScrollFrame();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -39,7 +39,7 @@ JX.behavior('phabricator-watch-anchor', function() {
|
||||||
var n = 50;
|
var n = 50;
|
||||||
var try_anchor_again = function () {
|
var try_anchor_again = function () {
|
||||||
try {
|
try {
|
||||||
window.scrollTo(0, JX.$V(JX.$(anchor)).y - 60);
|
JX.DOM.scrollToPosition(0, JX.$V(JX.$(anchor)).y - 60);
|
||||||
defer_highlight();
|
defer_highlight();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (n--) {
|
if (n--) {
|
||||||
|
|
Loading…
Reference in a new issue