mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-10 08:52:39 +01:00
Scroll parent containers when objects are dragged near the edge of the field of view
Summary: Ref T5240. This probably has some bugs and doesn't quite work in Firefox (fine on boards, not quite on the task list -- some issue with body or document being special, I think). I think this is close enough that we can throw it out there and see how users manage to break it, though. It's not worse than what we've got now? I think? Test Plan: dragged things near the edge of other things they seemed to move around OK Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T5240 Differential Revision: https://secure.phabricator.com/D10188
This commit is contained in:
parent
61318a8119
commit
e433a09fde
3 changed files with 171 additions and 29 deletions
|
@ -8,7 +8,7 @@
|
||||||
return array(
|
return array(
|
||||||
'names' => array(
|
'names' => array(
|
||||||
'core.pkg.css' => '764d4c80',
|
'core.pkg.css' => '764d4c80',
|
||||||
'core.pkg.js' => '5b832397',
|
'core.pkg.js' => '53c6a7c5',
|
||||||
'darkconsole.pkg.js' => 'e7393ebb',
|
'darkconsole.pkg.js' => 'e7393ebb',
|
||||||
'differential.pkg.css' => '2de124c9',
|
'differential.pkg.css' => '2de124c9',
|
||||||
'differential.pkg.js' => '5c2ba922',
|
'differential.pkg.js' => '5c2ba922',
|
||||||
|
@ -413,7 +413,7 @@ return array(
|
||||||
'rsrc/js/application/phortune/phortune-credit-card-form.js' => '2290aeef',
|
'rsrc/js/application/phortune/phortune-credit-card-form.js' => '2290aeef',
|
||||||
'rsrc/js/application/policy/behavior-policy-control.js' => 'ae45872f',
|
'rsrc/js/application/policy/behavior-policy-control.js' => 'ae45872f',
|
||||||
'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '5e9f347c',
|
'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '5e9f347c',
|
||||||
'rsrc/js/application/projects/behavior-project-boards.js' => '16c76360',
|
'rsrc/js/application/projects/behavior-project-boards.js' => 'c05fb42a',
|
||||||
'rsrc/js/application/projects/behavior-project-create.js' => '065227cc',
|
'rsrc/js/application/projects/behavior-project-create.js' => '065227cc',
|
||||||
'rsrc/js/application/projects/behavior-reorder-columns.js' => 'e1d25dfb',
|
'rsrc/js/application/projects/behavior-reorder-columns.js' => 'e1d25dfb',
|
||||||
'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf',
|
'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf',
|
||||||
|
@ -446,7 +446,7 @@ return array(
|
||||||
'rsrc/js/application/uiexample/notification-example.js' => '8ce821c5',
|
'rsrc/js/application/uiexample/notification-example.js' => '8ce821c5',
|
||||||
'rsrc/js/core/Busy.js' => '59a7976a',
|
'rsrc/js/core/Busy.js' => '59a7976a',
|
||||||
'rsrc/js/core/DragAndDropFileUpload.js' => 'ad10aeac',
|
'rsrc/js/core/DragAndDropFileUpload.js' => 'ad10aeac',
|
||||||
'rsrc/js/core/DraggableList.js' => '1fe26f18',
|
'rsrc/js/core/DraggableList.js' => '8905523d',
|
||||||
'rsrc/js/core/FileUpload.js' => '477359c8',
|
'rsrc/js/core/FileUpload.js' => '477359c8',
|
||||||
'rsrc/js/core/Hovercard.js' => 'c6f720ff',
|
'rsrc/js/core/Hovercard.js' => 'c6f720ff',
|
||||||
'rsrc/js/core/KeyboardShortcut.js' => '1ae869f2',
|
'rsrc/js/core/KeyboardShortcut.js' => '1ae869f2',
|
||||||
|
@ -653,7 +653,7 @@ return array(
|
||||||
'javelin-behavior-phui-profile-menu' => '12884df9',
|
'javelin-behavior-phui-profile-menu' => '12884df9',
|
||||||
'javelin-behavior-policy-control' => 'ae45872f',
|
'javelin-behavior-policy-control' => 'ae45872f',
|
||||||
'javelin-behavior-policy-rule-editor' => '5e9f347c',
|
'javelin-behavior-policy-rule-editor' => '5e9f347c',
|
||||||
'javelin-behavior-project-boards' => '16c76360',
|
'javelin-behavior-project-boards' => 'c05fb42a',
|
||||||
'javelin-behavior-project-create' => '065227cc',
|
'javelin-behavior-project-create' => '065227cc',
|
||||||
'javelin-behavior-quicksand-blacklist' => '7927a7d3',
|
'javelin-behavior-quicksand-blacklist' => '7927a7d3',
|
||||||
'javelin-behavior-recurring-edit' => '5f1c4d5f',
|
'javelin-behavior-recurring-edit' => '5f1c4d5f',
|
||||||
|
@ -741,7 +741,7 @@ return array(
|
||||||
'phabricator-countdown-css' => 'e7544472',
|
'phabricator-countdown-css' => 'e7544472',
|
||||||
'phabricator-dashboard-css' => 'eb458607',
|
'phabricator-dashboard-css' => 'eb458607',
|
||||||
'phabricator-drag-and-drop-file-upload' => 'ad10aeac',
|
'phabricator-drag-and-drop-file-upload' => 'ad10aeac',
|
||||||
'phabricator-draggable-list' => '1fe26f18',
|
'phabricator-draggable-list' => '8905523d',
|
||||||
'phabricator-fatal-config-template-css' => '8e6c6fcd',
|
'phabricator-fatal-config-template-css' => '8e6c6fcd',
|
||||||
'phabricator-feed-css' => 'ecd4ec57',
|
'phabricator-feed-css' => 'ecd4ec57',
|
||||||
'phabricator-file-upload' => '477359c8',
|
'phabricator-file-upload' => '477359c8',
|
||||||
|
@ -953,15 +953,6 @@ return array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'javelin-history',
|
'javelin-history',
|
||||||
),
|
),
|
||||||
'16c76360' => array(
|
|
||||||
'javelin-behavior',
|
|
||||||
'javelin-dom',
|
|
||||||
'javelin-util',
|
|
||||||
'javelin-vector',
|
|
||||||
'javelin-stratcom',
|
|
||||||
'javelin-workflow',
|
|
||||||
'phabricator-draggable-list',
|
|
||||||
),
|
|
||||||
'1ad0a787' => array(
|
'1ad0a787' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-reactor',
|
'javelin-reactor',
|
||||||
|
@ -1007,14 +998,6 @@ return array(
|
||||||
'phuix-icon-view',
|
'phuix-icon-view',
|
||||||
'javelin-behavior-phabricator-gesture',
|
'javelin-behavior-phabricator-gesture',
|
||||||
),
|
),
|
||||||
'1fe26f18' => array(
|
|
||||||
'javelin-install',
|
|
||||||
'javelin-dom',
|
|
||||||
'javelin-stratcom',
|
|
||||||
'javelin-util',
|
|
||||||
'javelin-vector',
|
|
||||||
'javelin-magical-init',
|
|
||||||
),
|
|
||||||
'21ba5861' => array(
|
'21ba5861' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
@ -1492,6 +1475,14 @@ return array(
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
),
|
),
|
||||||
|
'8905523d' => array(
|
||||||
|
'javelin-install',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-stratcom',
|
||||||
|
'javelin-util',
|
||||||
|
'javelin-vector',
|
||||||
|
'javelin-magical-init',
|
||||||
|
),
|
||||||
'8a41885b' => array(
|
'8a41885b' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
@ -1781,6 +1772,15 @@ return array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
),
|
),
|
||||||
|
'c05fb42a' => array(
|
||||||
|
'javelin-behavior',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-util',
|
||||||
|
'javelin-vector',
|
||||||
|
'javelin-stratcom',
|
||||||
|
'javelin-workflow',
|
||||||
|
'phabricator-draggable-list',
|
||||||
|
),
|
||||||
'c1700f6f' => array(
|
'c1700f6f' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
|
|
|
@ -232,6 +232,7 @@ JX.behavior('project-boards', function(config, statics) {
|
||||||
for (ii = 0; ii < cols.length; ii++) {
|
for (ii = 0; ii < cols.length; ii++) {
|
||||||
var list = new JX.DraggableList('project-card', cols[ii])
|
var list = new JX.DraggableList('project-card', cols[ii])
|
||||||
.setFindItemsHandler(JX.bind(null, finditems, cols[ii]))
|
.setFindItemsHandler(JX.bind(null, finditems, cols[ii]))
|
||||||
|
.setOuterContainer(JX.$(config.boardID))
|
||||||
.setCanDragX(true);
|
.setCanDragX(true);
|
||||||
|
|
||||||
list.listen('didSend', JX.bind(list, onupdate, cols[ii]));
|
list.listen('didSend', JX.bind(list, onupdate, cols[ii]));
|
||||||
|
|
|
@ -39,7 +39,8 @@ JX.install('DraggableList', {
|
||||||
|
|
||||||
properties : {
|
properties : {
|
||||||
findItemsHandler: null,
|
findItemsHandler: null,
|
||||||
canDragX: false
|
canDragX: false,
|
||||||
|
outerContainer: null
|
||||||
},
|
},
|
||||||
|
|
||||||
members : {
|
members : {
|
||||||
|
@ -51,10 +52,15 @@ JX.install('DraggableList', {
|
||||||
_ghostHandler : null,
|
_ghostHandler : null,
|
||||||
_ghostNode : null,
|
_ghostNode : null,
|
||||||
_group : null,
|
_group : null,
|
||||||
_lastMousePosition: null,
|
_cursorPosition: null,
|
||||||
|
_cursorOrigin: null,
|
||||||
|
_cursorScroll: null,
|
||||||
_frame: null,
|
_frame: null,
|
||||||
_clone: null,
|
_clone: null,
|
||||||
_offset: null,
|
_offset: null,
|
||||||
|
_autoscroll: null,
|
||||||
|
_autoscroller: null,
|
||||||
|
_autotimer: null,
|
||||||
|
|
||||||
getRootNode : function() {
|
getRootNode : function() {
|
||||||
return this._root;
|
return this._root;
|
||||||
|
@ -177,6 +183,10 @@ JX.install('DraggableList', {
|
||||||
|
|
||||||
var drag = e.getNode(this._sigil);
|
var drag = e.getNode(this._sigil);
|
||||||
|
|
||||||
|
this._autoscroll = {};
|
||||||
|
this._autoscroller = setInterval(JX.bind(this, this._onautoscroll), 10);
|
||||||
|
this._autotimer = null;
|
||||||
|
|
||||||
for (var ii = 0; ii < this._group.length; ii++) {
|
for (var ii = 0; ii < this._group.length; ii++) {
|
||||||
this._group[ii]._clearTarget();
|
this._group[ii]._clearTarget();
|
||||||
}
|
}
|
||||||
|
@ -398,14 +408,16 @@ JX.install('DraggableList', {
|
||||||
// reuse the known position.
|
// reuse the known position.
|
||||||
|
|
||||||
if (e.getType() == 'mousemove') {
|
if (e.getType() == 'mousemove') {
|
||||||
this._lastMousePosition = JX.$V(e);
|
this._cursorPosition = JX.$V(e);
|
||||||
|
this._cursorOrigin = JX.$V(e);
|
||||||
|
this._cursorScroll = JX.Vector.getScroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._dragging) {
|
if (!this._dragging) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._lastMousePosition) {
|
if (!this._cursorPosition) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,9 +425,17 @@ JX.install('DraggableList', {
|
||||||
// If this is a scroll event, the positions of drag targets may have
|
// If this is a scroll event, the positions of drag targets may have
|
||||||
// changed.
|
// changed.
|
||||||
this._dirtyTargetCache();
|
this._dirtyTargetCache();
|
||||||
|
|
||||||
|
// Correct the cursor position to account for scrolling.
|
||||||
|
var s = JX.Vector.getScroll();
|
||||||
|
this._cursorPosition = new JX.$V(
|
||||||
|
this._cursorOrigin.x - (this._cursorScroll.x - s.x),
|
||||||
|
this._cursorOrigin.y - (this._cursorScroll.y - s.y));
|
||||||
}
|
}
|
||||||
|
|
||||||
var p = JX.$V(this._lastMousePosition.x, this._lastMousePosition.y);
|
this._updateAutoscroll(this._cursorPosition);
|
||||||
|
|
||||||
|
var p = JX.$V(this._cursorPosition.x, this._cursorPosition.y);
|
||||||
|
|
||||||
var group = this._group;
|
var group = this._group;
|
||||||
var target_list = this._getTargetList(p);
|
var target_list = this._getTargetList(p);
|
||||||
|
@ -454,6 +474,60 @@ JX.install('DraggableList', {
|
||||||
e.kill();
|
e.kill();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_updateAutoscroll: function(p) {
|
||||||
|
var container = this._dragging.parentNode;
|
||||||
|
var autoscroll = {};
|
||||||
|
|
||||||
|
var outer = this.getOuterContainer();
|
||||||
|
|
||||||
|
var cpos;
|
||||||
|
var cdim;
|
||||||
|
|
||||||
|
while (container) {
|
||||||
|
if (outer && (container == outer)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
cpos = JX.Vector.getPos(container);
|
||||||
|
cdim = JX.Vector.getDim(container);
|
||||||
|
if (container == document.body) {
|
||||||
|
cdim = JX.Vector.getViewport();
|
||||||
|
cpos.x += container.scrollLeft;
|
||||||
|
cpos.y += container.scrollTop;
|
||||||
|
}
|
||||||
|
} catch (ignored) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fuzz = 64;
|
||||||
|
|
||||||
|
if (p.y <= cpos.y + fuzz) {
|
||||||
|
autoscroll.up = container;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.y >= cpos.y + cdim.y - fuzz) {
|
||||||
|
autoscroll.down = container;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.x <= cpos.x + fuzz) {
|
||||||
|
autoscroll.left = container;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.x >= cpos.x + cdim.x - fuzz) {
|
||||||
|
autoscroll.right = container;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (container == document.body) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
container = container.parentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._autoscroll = autoscroll;
|
||||||
|
},
|
||||||
|
|
||||||
_onkey: function(e) {
|
_onkey: function(e) {
|
||||||
// Cancel any current drag if the user presses escape.
|
// Cancel any current drag if the user presses escape.
|
||||||
if (this._dragging && (e.getSpecialKey() == 'esc')) {
|
if (this._dragging && (e.getSpecialKey() == 'esc')) {
|
||||||
|
@ -464,6 +538,10 @@ JX.install('DraggableList', {
|
||||||
},
|
},
|
||||||
|
|
||||||
_ondrop : function(e) {
|
_ondrop : function(e) {
|
||||||
|
if (this._dragging) {
|
||||||
|
e.kill();
|
||||||
|
}
|
||||||
|
|
||||||
var p = JX.$V(e);
|
var p = JX.$V(e);
|
||||||
this._drop(p);
|
this._drop(p);
|
||||||
},
|
},
|
||||||
|
@ -475,6 +553,8 @@ JX.install('DraggableList', {
|
||||||
|
|
||||||
var dragging = this._dragging;
|
var dragging = this._dragging;
|
||||||
this._dragging = null;
|
this._dragging = null;
|
||||||
|
clearInterval(this._autoscroller);
|
||||||
|
this._autoscroller = null;
|
||||||
|
|
||||||
JX.DOM.remove(this._frame);
|
JX.DOM.remove(this._frame);
|
||||||
this._frame = null;
|
this._frame = null;
|
||||||
|
@ -512,11 +592,72 @@ JX.install('DraggableList', {
|
||||||
JX.DOM.alterClass(dragging, 'drag-dragging', false);
|
JX.DOM.alterClass(dragging, 'drag-dragging', false);
|
||||||
JX.Tooltip.unlock();
|
JX.Tooltip.unlock();
|
||||||
|
|
||||||
e.kill();
|
|
||||||
|
|
||||||
this.invoke('didEndDrag', dragging);
|
this.invoke('didEndDrag', dragging);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onautoscroll: function() {
|
||||||
|
var u = this._autoscroll.up;
|
||||||
|
var d = this._autoscroll.down;
|
||||||
|
var l = this._autoscroll.left;
|
||||||
|
var r = this._autoscroll.right;
|
||||||
|
|
||||||
|
var now = +new Date();
|
||||||
|
|
||||||
|
if (!this._autotimer) {
|
||||||
|
this._autotimer = now;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var delta = now - this._autotimer;
|
||||||
|
this._autotimer = now;
|
||||||
|
|
||||||
|
var amount = 12 * (delta / 10);
|
||||||
|
|
||||||
|
if (u && (u != d)) {
|
||||||
|
this._tryScroll(this._dragging, u, 'scrollTop', amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d && (d != u)) {
|
||||||
|
this._tryScroll(this._dragging, d, 'scrollTop', -amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (l && (l != r)) {
|
||||||
|
this._tryScroll(this._dragging, l, 'scrollLeft', amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r && (r != l)) {
|
||||||
|
this._tryScroll(this._dragging, r, 'scrollLeft', -amount);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Walk up the tree from a node to some parent, trying to scroll every
|
||||||
|
* container. Stop when we find a container which we're able to scroll.
|
||||||
|
*/
|
||||||
|
_tryScroll: function(from, to, property, amount) {
|
||||||
|
var value;
|
||||||
|
|
||||||
|
var container = from.parentNode;
|
||||||
|
while (container) {
|
||||||
|
// Read the current scroll value.
|
||||||
|
value = container[property];
|
||||||
|
|
||||||
|
// Try to scroll.
|
||||||
|
container[property] -= amount;
|
||||||
|
|
||||||
|
// If we scrolled it, we're all done.
|
||||||
|
if (container[property] != value) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (container == to) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
container = container.parentNode;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
lock : function() {
|
lock : function() {
|
||||||
for (var ii = 0; ii < this._group.length; ii++) {
|
for (var ii = 0; ii < this._group.length; ii++) {
|
||||||
this._group[ii]._lock();
|
this._group[ii]._lock();
|
||||||
|
|
Loading…
Reference in a new issue