@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+100 -12
+1
src/view/page/PhabricatorStandardPageView.php
··· 344 344 'scrollbar', 345 345 array( 346 346 'nodeID' => 'phabricator-standard-page', 347 + 'isMainContent' => true, 347 348 )); 348 349 349 350 $main_page = phutil_tag(
+60 -1
webroot/rsrc/externals/javelin/lib/DOM.js
··· 336 336 _autoid : 0, 337 337 _uniqid : 0, 338 338 _metrics : {}, 339 + _frameNode: null, 340 + _contentNode: null, 339 341 340 342 341 343 /* -( Changing DOM Content )----------------------------------------------- */ ··· 936 938 937 939 938 940 /** 941 + * Set specific nodes as content and frame nodes for the document. 942 + * 943 + * This will cause @{method:scrollTo} and @{method:scrollToPosition} to 944 + * affect the given frame node instead of the window. This is useful if the 945 + * page content is broken into multiple panels which scroll independently. 946 + * 947 + * Normally, both nodes are the document body. 948 + * 949 + * @task view 950 + * @param Node Node to set as the scroll frame. 951 + * @param Node Node to set as the content frame. 952 + * @return void 953 + */ 954 + setContentFrame: function(frame_node, content_node) { 955 + JX.DOM._frameNode = frame_node; 956 + JX.DOM._contentNode = content_node; 957 + }, 958 + 959 + 960 + /** 961 + * Get the current content frame, or `document.body` if one has not been 962 + * set. 963 + * 964 + * @task view 965 + * @return Node The node which frames the main page content. 966 + * @return void 967 + */ 968 + getContentFrame: function() { 969 + return JX.DOM._contentNode || document.body; 970 + }, 971 + 972 + /** 939 973 * Scroll to the position of an element in the document. 974 + * 975 + * If @{method:setContentFrame} has been used to set a frame, that node is 976 + * scrolled. 977 + * 940 978 * @task view 941 979 * @param Node Node to move document scroll position to, if possible. 942 980 * @return void 943 981 */ 944 982 scrollTo : function(node) { 945 - window.scrollTo(0, JX.$V(node).y); 983 + JX.DOM._scrollToPosition(0, JX.$V(node).y); 984 + }, 985 + 986 + /** 987 + * Scroll to a specific position in the document. 988 + * 989 + * If @{method:setContentFrame} has been used to set a frame, that node is 990 + * scrolled. 991 + * 992 + * @task view 993 + * @param int X position, in pixels. 994 + * @param int Y position, in pixels. 995 + * @return void 996 + */ 997 + scrollToPosition: function(x, y) { 998 + var self = JX.DOM; 999 + if (self._frameNode) { 1000 + self._frameNode.scrollLeft = x; 1001 + self._frameNode.scrollTop = y; 1002 + } else { 1003 + window.scrollTo(x, y); 1004 + } 946 1005 }, 947 1006 948 1007 _getAutoID : function(node) {
+22 -1
webroot/rsrc/externals/javelin/lib/Scrollbar.js
··· 25 25 JX.install('Scrollbar', { 26 26 27 27 construct: function(frame) { 28 + this._frame = frame; 29 + 28 30 // Before doing anything, check if the scrollbar control has a measurable 29 31 // width. If it doesn't, we're already in an environment with an aesthetic 30 32 // scrollbar (like Safari on OSX with no mouse connected, or an iPhone) ··· 60 62 var viewport = JX.$N('div', {className: 'jx-scrollbar-viewport'}, content); 61 63 JX.DOM.appendContent(frame, viewport); 62 64 63 - this._frame = frame; 64 65 this._viewport = viewport; 65 66 this._content = content; 66 67 ··· 132 133 _timeout: null, 133 134 _dragOrigin: null, 134 135 _scrollOrigin: null, 136 + 137 + 138 + /** 139 + * Mark this content as the scroll frame. 140 + * 141 + * This changes the behavior of the @{class:JX.DOM} scroll functions so the 142 + * continue to work properly if the main page content is reframed to scroll 143 + * independently. 144 + */ 145 + setAsScrollFrame: function() { 146 + if (this._viewport) { 147 + // If we activated the scrollbar, the viewport and content nodes become 148 + // the new scroll and content frames. 149 + JX.DOM.setContentFrame(this._viewport, this._content); 150 + } else { 151 + // Otherwise, the unaltered content frame is both the scroll frame and 152 + // content frame. 153 + JX.DOM.setContentFrame(this._frame, this._frame); 154 + } 155 + }, 135 156 136 157 137 158 /**
+1 -1
webroot/rsrc/externals/javelin/lib/Workflow.js
··· 204 204 // The `focus()` call may have scrolled the window. Scroll it back to 205 205 // where it was before -- we want to focus the control, but not adjust 206 206 // the scroll position. 207 - window.scrollTo(s.x, s.y); 207 + JX.DOM.scrollToPosition(s.x, s.y); 208 208 209 209 } else if (this.getHandler()) { 210 210 this.getHandler()(r);
+1 -1
webroot/rsrc/js/application/differential/ChangesetViewManager.js
··· 257 257 if (near_bot || above_mid) { 258 258 // Figure out how much taller the document got. 259 259 var delta = (JX.Vector.getDocument().y - old_dim.y); 260 - window.scrollTo(old_pos.x, old_pos.y + delta); 260 + JX.DOM.scrollToPosition(old_pos.x, old_pos.y + delta); 261 261 } 262 262 } 263 263 this._stabilize = false;
+1 -1
webroot/rsrc/js/application/diffusion/behavior-jump-to.js
··· 8 8 JX.behavior('diffusion-jump-to', function(config) { 9 9 10 10 setTimeout(function() { 11 - window.scrollTo(0, JX.$V(JX.$(config.target)).y - 100); 11 + JX.DOM.scrollTo(0, JX.$V(JX.$(config.target)).y - 100); 12 12 }, 0); 13 13 14 14 });
+1 -1
webroot/rsrc/js/application/releeph/releeph-request-state-change.js
··· 22 22 23 23 if (keynav_cursor < 0) { 24 24 keynav_cursor = -1; 25 - window.scrollTo(0); 25 + JX.DOM.scrollToPosition(0, 0); 26 26 keynavMarkup(); 27 27 return; 28 28 }
+8 -4
webroot/rsrc/js/core/KeyboardShortcutManager.js
··· 66 66 * Scroll an element into view. 67 67 */ 68 68 scrollTo : function(node) { 69 - window.scrollTo(0, JX.$V(node).y - 60); 69 + var scroll_distance = JX.Vector.getAggregateScrollForNode(node); 70 + var node_position = JX.$V(node); 71 + JX.DOM.scrollToPosition(0, node_position.y + scroll_distance.y - 60); 70 72 }, 71 73 72 74 /** ··· 91 93 92 94 // Outset the reticle some pixels away from the element, so there's some 93 95 // space between the focused element and the outline. 94 - var p = JX.Vector.getPos(node); 95 - p.add(-4, -4).setPos(r); 96 + var p = JX.Vector.getPos(node); 97 + var s = JX.Vector.getAggregateScrollForNode(node); 98 + 99 + p.add(s).add(-4, -4).setPos(r); 96 100 // Compute the size we need to extend to the full extent of the focused 97 101 // nodes. 98 102 JX.Vector.getPos(extended_node) ··· 100 104 .add(JX.Vector.getDim(extended_node)) 101 105 .add(8, 8) 102 106 .setDim(r); 103 - document.body.appendChild(r); 107 + JX.DOM.getContentFrame().appendChild(r); 104 108 105 109 this._focusReticle = r; 106 110 },
+4 -1
webroot/rsrc/js/core/behavior-scrollbar.js
··· 5 5 */ 6 6 7 7 JX.behavior('scrollbar', function(config) { 8 - new JX.Scrollbar(JX.$(config.nodeID)); 8 + var bar = new JX.Scrollbar(JX.$(config.nodeID)); 9 + if (config.isMainContent) { 10 + bar.setAsScrollFrame(); 11 + } 9 12 });
+1 -1
webroot/rsrc/js/core/behavior-watch-anchor.js
··· 39 39 var n = 50; 40 40 var try_anchor_again = function () { 41 41 try { 42 - window.scrollTo(0, JX.$V(JX.$(anchor)).y - 60); 42 + JX.DOM.scrollToPosition(0, JX.$V(JX.$(anchor)).y - 60); 43 43 defer_highlight(); 44 44 } catch (e) { 45 45 if (n--) {