@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.

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

+171 -29
+22 -22
resources/celerity/map.php
··· 8 8 return array( 9 9 'names' => array( 10 10 'core.pkg.css' => '764d4c80', 11 - 'core.pkg.js' => '5b832397', 11 + 'core.pkg.js' => '53c6a7c5', 12 12 'darkconsole.pkg.js' => 'e7393ebb', 13 13 'differential.pkg.css' => '2de124c9', 14 14 'differential.pkg.js' => '5c2ba922', ··· 413 413 'rsrc/js/application/phortune/phortune-credit-card-form.js' => '2290aeef', 414 414 'rsrc/js/application/policy/behavior-policy-control.js' => 'ae45872f', 415 415 'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '5e9f347c', 416 - 'rsrc/js/application/projects/behavior-project-boards.js' => '16c76360', 416 + 'rsrc/js/application/projects/behavior-project-boards.js' => 'c05fb42a', 417 417 'rsrc/js/application/projects/behavior-project-create.js' => '065227cc', 418 418 'rsrc/js/application/projects/behavior-reorder-columns.js' => 'e1d25dfb', 419 419 'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf', ··· 446 446 'rsrc/js/application/uiexample/notification-example.js' => '8ce821c5', 447 447 'rsrc/js/core/Busy.js' => '59a7976a', 448 448 'rsrc/js/core/DragAndDropFileUpload.js' => 'ad10aeac', 449 - 'rsrc/js/core/DraggableList.js' => '1fe26f18', 449 + 'rsrc/js/core/DraggableList.js' => '8905523d', 450 450 'rsrc/js/core/FileUpload.js' => '477359c8', 451 451 'rsrc/js/core/Hovercard.js' => 'c6f720ff', 452 452 'rsrc/js/core/KeyboardShortcut.js' => '1ae869f2', ··· 653 653 'javelin-behavior-phui-profile-menu' => '12884df9', 654 654 'javelin-behavior-policy-control' => 'ae45872f', 655 655 'javelin-behavior-policy-rule-editor' => '5e9f347c', 656 - 'javelin-behavior-project-boards' => '16c76360', 656 + 'javelin-behavior-project-boards' => 'c05fb42a', 657 657 'javelin-behavior-project-create' => '065227cc', 658 658 'javelin-behavior-quicksand-blacklist' => '7927a7d3', 659 659 'javelin-behavior-recurring-edit' => '5f1c4d5f', ··· 741 741 'phabricator-countdown-css' => 'e7544472', 742 742 'phabricator-dashboard-css' => 'eb458607', 743 743 'phabricator-drag-and-drop-file-upload' => 'ad10aeac', 744 - 'phabricator-draggable-list' => '1fe26f18', 744 + 'phabricator-draggable-list' => '8905523d', 745 745 'phabricator-fatal-config-template-css' => '8e6c6fcd', 746 746 'phabricator-feed-css' => 'ecd4ec57', 747 747 'phabricator-file-upload' => '477359c8', ··· 953 953 'javelin-dom', 954 954 'javelin-history', 955 955 ), 956 - '16c76360' => array( 957 - 'javelin-behavior', 958 - 'javelin-dom', 959 - 'javelin-util', 960 - 'javelin-vector', 961 - 'javelin-stratcom', 962 - 'javelin-workflow', 963 - 'phabricator-draggable-list', 964 - ), 965 956 '1ad0a787' => array( 966 957 'javelin-install', 967 958 'javelin-reactor', ··· 1007 998 'phuix-icon-view', 1008 999 'javelin-behavior-phabricator-gesture', 1009 1000 ), 1010 - '1fe26f18' => array( 1011 - 'javelin-install', 1012 - 'javelin-dom', 1013 - 'javelin-stratcom', 1014 - 'javelin-util', 1015 - 'javelin-vector', 1016 - 'javelin-magical-init', 1017 - ), 1018 1001 '21ba5861' => array( 1019 1002 'javelin-behavior', 1020 1003 'javelin-dom', ··· 1492 1475 'javelin-stratcom', 1493 1476 'javelin-dom', 1494 1477 ), 1478 + '8905523d' => array( 1479 + 'javelin-install', 1480 + 'javelin-dom', 1481 + 'javelin-stratcom', 1482 + 'javelin-util', 1483 + 'javelin-vector', 1484 + 'javelin-magical-init', 1485 + ), 1495 1486 '8a41885b' => array( 1496 1487 'javelin-install', 1497 1488 'javelin-dom', ··· 1780 1771 'bff6884b' => array( 1781 1772 'javelin-install', 1782 1773 'javelin-dom', 1774 + ), 1775 + 'c05fb42a' => array( 1776 + 'javelin-behavior', 1777 + 'javelin-dom', 1778 + 'javelin-util', 1779 + 'javelin-vector', 1780 + 'javelin-stratcom', 1781 + 'javelin-workflow', 1782 + 'phabricator-draggable-list', 1783 1783 ), 1784 1784 'c1700f6f' => array( 1785 1785 'javelin-install',
+1
webroot/rsrc/js/application/projects/behavior-project-boards.js
··· 232 232 for (ii = 0; ii < cols.length; ii++) { 233 233 var list = new JX.DraggableList('project-card', cols[ii]) 234 234 .setFindItemsHandler(JX.bind(null, finditems, cols[ii])) 235 + .setOuterContainer(JX.$(config.boardID)) 235 236 .setCanDragX(true); 236 237 237 238 list.listen('didSend', JX.bind(list, onupdate, cols[ii]));
+148 -7
webroot/rsrc/js/core/DraggableList.js
··· 39 39 40 40 properties : { 41 41 findItemsHandler: null, 42 - canDragX: false 42 + canDragX: false, 43 + outerContainer: null 43 44 }, 44 45 45 46 members : { ··· 51 52 _ghostHandler : null, 52 53 _ghostNode : null, 53 54 _group : null, 54 - _lastMousePosition: null, 55 + _cursorPosition: null, 56 + _cursorOrigin: null, 57 + _cursorScroll: null, 55 58 _frame: null, 56 59 _clone: null, 57 60 _offset: null, 61 + _autoscroll: null, 62 + _autoscroller: null, 63 + _autotimer: null, 58 64 59 65 getRootNode : function() { 60 66 return this._root; ··· 176 182 e.kill(); 177 183 178 184 var drag = e.getNode(this._sigil); 185 + 186 + this._autoscroll = {}; 187 + this._autoscroller = setInterval(JX.bind(this, this._onautoscroll), 10); 188 + this._autotimer = null; 179 189 180 190 for (var ii = 0; ii < this._group.length; ii++) { 181 191 this._group[ii]._clearTarget(); ··· 398 408 // reuse the known position. 399 409 400 410 if (e.getType() == 'mousemove') { 401 - this._lastMousePosition = JX.$V(e); 411 + this._cursorPosition = JX.$V(e); 412 + this._cursorOrigin = JX.$V(e); 413 + this._cursorScroll = JX.Vector.getScroll(); 402 414 } 403 415 404 416 if (!this._dragging) { 405 417 return; 406 418 } 407 419 408 - if (!this._lastMousePosition) { 420 + if (!this._cursorPosition) { 409 421 return; 410 422 } 411 423 ··· 413 425 // If this is a scroll event, the positions of drag targets may have 414 426 // changed. 415 427 this._dirtyTargetCache(); 428 + 429 + // Correct the cursor position to account for scrolling. 430 + var s = JX.Vector.getScroll(); 431 + this._cursorPosition = new JX.$V( 432 + this._cursorOrigin.x - (this._cursorScroll.x - s.x), 433 + this._cursorOrigin.y - (this._cursorScroll.y - s.y)); 416 434 } 417 435 418 - var p = JX.$V(this._lastMousePosition.x, this._lastMousePosition.y); 436 + this._updateAutoscroll(this._cursorPosition); 437 + 438 + var p = JX.$V(this._cursorPosition.x, this._cursorPosition.y); 419 439 420 440 var group = this._group; 421 441 var target_list = this._getTargetList(p); ··· 454 474 e.kill(); 455 475 }, 456 476 477 + _updateAutoscroll: function(p) { 478 + var container = this._dragging.parentNode; 479 + var autoscroll = {}; 480 + 481 + var outer = this.getOuterContainer(); 482 + 483 + var cpos; 484 + var cdim; 485 + 486 + while (container) { 487 + if (outer && (container == outer)) { 488 + break; 489 + } 490 + 491 + try { 492 + cpos = JX.Vector.getPos(container); 493 + cdim = JX.Vector.getDim(container); 494 + if (container == document.body) { 495 + cdim = JX.Vector.getViewport(); 496 + cpos.x += container.scrollLeft; 497 + cpos.y += container.scrollTop; 498 + } 499 + } catch (ignored) { 500 + break; 501 + } 502 + 503 + var fuzz = 64; 504 + 505 + if (p.y <= cpos.y + fuzz) { 506 + autoscroll.up = container; 507 + } 508 + 509 + if (p.y >= cpos.y + cdim.y - fuzz) { 510 + autoscroll.down = container; 511 + } 512 + 513 + if (p.x <= cpos.x + fuzz) { 514 + autoscroll.left = container; 515 + } 516 + 517 + if (p.x >= cpos.x + cdim.x - fuzz) { 518 + autoscroll.right = container; 519 + } 520 + 521 + if (container == document.body) { 522 + break; 523 + } 524 + 525 + container = container.parentNode; 526 + } 527 + 528 + this._autoscroll = autoscroll; 529 + }, 530 + 457 531 _onkey: function(e) { 458 532 // Cancel any current drag if the user presses escape. 459 533 if (this._dragging && (e.getSpecialKey() == 'esc')) { ··· 464 538 }, 465 539 466 540 _ondrop : function(e) { 541 + if (this._dragging) { 542 + e.kill(); 543 + } 544 + 467 545 var p = JX.$V(e); 468 546 this._drop(p); 469 547 }, ··· 475 553 476 554 var dragging = this._dragging; 477 555 this._dragging = null; 556 + clearInterval(this._autoscroller); 557 + this._autoscroller = null; 478 558 479 559 JX.DOM.remove(this._frame); 480 560 this._frame = null; ··· 512 592 JX.DOM.alterClass(dragging, 'drag-dragging', false); 513 593 JX.Tooltip.unlock(); 514 594 515 - e.kill(); 595 + this.invoke('didEndDrag', dragging); 596 + }, 597 + 598 + _onautoscroll: function() { 599 + var u = this._autoscroll.up; 600 + var d = this._autoscroll.down; 601 + var l = this._autoscroll.left; 602 + var r = this._autoscroll.right; 603 + 604 + var now = +new Date(); 605 + 606 + if (!this._autotimer) { 607 + this._autotimer = now; 608 + return; 609 + } 610 + 611 + var delta = now - this._autotimer; 612 + this._autotimer = now; 613 + 614 + var amount = 12 * (delta / 10); 615 + 616 + if (u && (u != d)) { 617 + this._tryScroll(this._dragging, u, 'scrollTop', amount); 618 + } 619 + 620 + if (d && (d != u)) { 621 + this._tryScroll(this._dragging, d, 'scrollTop', -amount); 622 + } 623 + 624 + if (l && (l != r)) { 625 + this._tryScroll(this._dragging, l, 'scrollLeft', amount); 626 + } 627 + 628 + if (r && (r != l)) { 629 + this._tryScroll(this._dragging, r, 'scrollLeft', -amount); 630 + } 631 + }, 632 + 633 + /** 634 + * Walk up the tree from a node to some parent, trying to scroll every 635 + * container. Stop when we find a container which we're able to scroll. 636 + */ 637 + _tryScroll: function(from, to, property, amount) { 638 + var value; 516 639 517 - this.invoke('didEndDrag', dragging); 640 + var container = from.parentNode; 641 + while (container) { 642 + // Read the current scroll value. 643 + value = container[property]; 644 + 645 + // Try to scroll. 646 + container[property] -= amount; 647 + 648 + // If we scrolled it, we're all done. 649 + if (container[property] != value) { 650 + break; 651 + } 652 + 653 + if (container == to) { 654 + break; 655 + } 656 + 657 + container = container.parentNode; 658 + } 518 659 }, 519 660 520 661 lock : function() {