Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14038527
D15160.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
12 KB
Referenced Files
None
Subscribers
None
D15160.id.diff
View Options
diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -7,8 +7,8 @@
*/
return array(
'names' => array(
- 'core.pkg.css' => '8b9c004a',
- 'core.pkg.js' => 'bf947f93',
+ 'core.pkg.css' => 'cd66e467',
+ 'core.pkg.js' => 'c5178ede',
'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => '2de124c9',
'differential.pkg.js' => '5c2ba922',
@@ -105,7 +105,7 @@
'rsrc/css/core/core.css' => '5b3563c8',
'rsrc/css/core/remarkup.css' => 'e1c8b32f',
'rsrc/css/core/syntax.css' => '9fd11da8',
- 'rsrc/css/core/z-index.css' => 'a36a45da',
+ 'rsrc/css/core/z-index.css' => '5c7025bf',
'rsrc/css/diviner/diviner-shared.css' => 'aa3656aa',
'rsrc/css/font/font-aleo.css' => '8bdb2835',
'rsrc/css/font/font-awesome.css' => 'c43323c5',
@@ -142,7 +142,7 @@
'rsrc/css/phui/phui-info-view.css' => '6d7c3509',
'rsrc/css/phui/phui-list.css' => '9da2aa00',
'rsrc/css/phui/phui-object-box.css' => '407eaf5a',
- 'rsrc/css/phui/phui-object-item-list-view.css' => 'febf4a79',
+ 'rsrc/css/phui/phui-object-item-list-view.css' => '699de05e',
'rsrc/css/phui/phui-pager.css' => 'bea33d23',
'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
'rsrc/css/phui/phui-profile-menu.css' => 'ab4fcf5f',
@@ -446,7 +446,7 @@
'rsrc/js/application/uiexample/notification-example.js' => '8ce821c5',
'rsrc/js/core/Busy.js' => '59a7976a',
'rsrc/js/core/DragAndDropFileUpload.js' => 'ad10aeac',
- 'rsrc/js/core/DraggableList.js' => '255d85da',
+ 'rsrc/js/core/DraggableList.js' => 'f1844746',
'rsrc/js/core/FileUpload.js' => '477359c8',
'rsrc/js/core/Hovercard.js' => 'c6f720ff',
'rsrc/js/core/KeyboardShortcut.js' => '1ae869f2',
@@ -741,7 +741,7 @@
'phabricator-countdown-css' => 'e7544472',
'phabricator-dashboard-css' => 'eb458607',
'phabricator-drag-and-drop-file-upload' => 'ad10aeac',
- 'phabricator-draggable-list' => '255d85da',
+ 'phabricator-draggable-list' => 'f1844746',
'phabricator-fatal-config-template-css' => '8e6c6fcd',
'phabricator-feed-css' => 'ecd4ec57',
'phabricator-file-upload' => '477359c8',
@@ -780,7 +780,7 @@
'phabricator-uiexample-reactor-select' => 'a155550f',
'phabricator-uiexample-reactor-sendclass' => '1def2711',
'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
- 'phabricator-zindex-css' => 'a36a45da',
+ 'phabricator-zindex-css' => '5c7025bf',
'phame-css' => '6d5b3682',
'pholio-css' => '95174bdd',
'pholio-edit-css' => '3ad9d1ee',
@@ -818,7 +818,7 @@
'phui-inline-comment-view-css' => '0fdb3667',
'phui-list-view-css' => '9da2aa00',
'phui-object-box-css' => '407eaf5a',
- 'phui-object-item-list-view-css' => 'febf4a79',
+ 'phui-object-item-list-view-css' => '699de05e',
'phui-pager-css' => 'bea33d23',
'phui-pinboard-view-css' => '2495140e',
'phui-profile-menu-css' => 'ab4fcf5f',
@@ -1021,14 +1021,6 @@
'phabricator-drag-and-drop-file-upload',
'phabricator-draggable-list',
),
- '255d85da' => array(
- 'javelin-install',
- 'javelin-dom',
- 'javelin-stratcom',
- 'javelin-util',
- 'javelin-vector',
- 'javelin-magical-init',
- ),
'2926fff2' => array(
'javelin-behavior',
'javelin-dom',
@@ -2019,6 +2011,14 @@
'javelin-workflow',
'javelin-json',
),
+ 'f1844746' => array(
+ 'javelin-install',
+ 'javelin-dom',
+ 'javelin-stratcom',
+ 'javelin-util',
+ 'javelin-vector',
+ 'javelin-magical-init',
+ ),
'f411b6ae' => array(
'javelin-behavior',
'javelin-stratcom',
diff --git a/src/view/phui/PHUIObjectItemView.php b/src/view/phui/PHUIObjectItemView.php
--- a/src/view/phui/PHUIObjectItemView.php
+++ b/src/view/phui/PHUIObjectItemView.php
@@ -383,17 +383,24 @@
),
$this->header);
- $header = javelin_tag(
+ // Wrap the header content in a <span> with the "slippery" sigil. This
+ // prevents us from beginning a drag if you click the text (like "T123"),
+ // but not if you click the white space after the header.
+ $header = phutil_tag(
'div',
array(
'class' => 'phui-object-item-name',
- 'sigil' => 'slippery',
),
- array(
- $this->headIcons,
- $header_name,
- $header_link,
- ));
+ javelin_tag(
+ 'span',
+ array(
+ 'sigil' => 'slippery',
+ ),
+ array(
+ $this->headIcons,
+ $header_name,
+ $header_link,
+ )));
$icons = array();
if ($this->icons) {
diff --git a/webroot/rsrc/css/core/z-index.css b/webroot/rsrc/css/core/z-index.css
--- a/webroot/rsrc/css/core/z-index.css
+++ b/webroot/rsrc/css/core/z-index.css
@@ -81,10 +81,6 @@
z-index: 5;
}
-.drag-dragging {
- z-index: 5;
-}
-
.phui-calendar-date-number {
z-index: 5;
}
@@ -114,6 +110,10 @@
z-index: 9;
}
+.drag-frame {
+ z-index: 10;
+}
+
.jx-mask {
z-index: 10;
}
diff --git a/webroot/rsrc/css/phui/phui-object-item-list-view.css b/webroot/rsrc/css/phui/phui-object-item-list-view.css
--- a/webroot/rsrc/css/phui/phui-object-item-list-view.css
+++ b/webroot/rsrc/css/phui/phui-object-item-list-view.css
@@ -593,15 +593,36 @@
}
.drag-dragging {
- position: relative;
- background: {$sh-yellowbackground};
- opacity: 0.9;
+ opacity: 0.25;
}
.drag-sending {
opacity: 0.5;
}
+.drag-clone,
+.drag-frame {
+ /* This allows mousewheel events to pass through the clone and frame while
+ they are being dragged. Without this, the mousewheel does not work during
+ a drag operation. */
+ pointer-events: none;
+}
+
+.drag-frame {
+ position: fixed;
+ overflow: hidden;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+}
+
+.drag-clone {
+ position: absolute;
+ list-style: none;
+}
+
+
/* - State ---------------------------------------------------------------------
Provides a list of object status or states, success or fail, etc
diff --git a/webroot/rsrc/js/core/DraggableList.js b/webroot/rsrc/js/core/DraggableList.js
--- a/webroot/rsrc/js/core/DraggableList.js
+++ b/webroot/rsrc/js/core/DraggableList.js
@@ -44,15 +44,15 @@
_root : null,
_dragging : null,
_locked : 0,
- _origin : null,
- _originScroll : null,
_target : null,
_targets : null,
_ghostHandler : null,
_ghostNode : null,
_group : null,
_lastMousePosition: null,
- _lastAdjust: null,
+ _frame: null,
+ _clone: null,
+ _offset: null,
getRootNode : function() {
return this._root;
@@ -131,7 +131,17 @@
}
}
- return handler();
+ var items = handler();
+
+ // Make sure the clone element is never included as a target.
+ for (var ii = 0; ii < items.length; ii++) {
+ if (items[ii] === this._clone) {
+ items.splice(ii, 1);
+ break;
+ }
+ }
+
+ return items;
},
_ondrag : function(e) {
@@ -167,24 +177,67 @@
e.kill();
- this._dragging = e.getNode(this._sigil);
- this._origin = JX.$V(e);
- this._originScroll = JX.Vector.getAggregateScrollForNode(this._dragging);
+ var drag = e.getNode(this._sigil);
for (var ii = 0; ii < this._group.length; ii++) {
this._group[ii]._clearTarget();
}
- if (!this.invoke('didBeginDrag', this._dragging).getPrevented()) {
- // Set the height of all the ghosts in the group. In the normal case,
- // this just sets this list's ghost height.
- for (var jj = 0; jj < this._group.length; jj++) {
- var ghost = this._group[jj].getGhostNode();
- ghost.style.height = JX.Vector.getDim(this._dragging).y + 'px';
- }
+ var pos = JX.$V(drag);
+ var dim = JX.Vector.getDim(drag);
- JX.DOM.alterClass(this._dragging, 'drag-dragging', true);
+ // Create and adjust the ghost nodes.
+ for (var jj = 0; jj < this._group.length; jj++) {
+ var ghost = this._group[jj].getGhostNode();
+ ghost.style.height = dim.y + 'px';
}
+
+ // Here's what's going on: we're cloning the thing that's being dragged.
+ // This is the "clone", stored in "this._clone". We're going to leave the
+ // original where it is in the document, and put the clone at top-level
+ // so it can be freely dragged around the whole document, even if it's
+ // inside a container with overflow hidden.
+
+ // Because the clone has been moved up, CSS classes which rely on some
+ // parent selector won't work. Draggable objects need to pick up all of
+ // their CSS properties without relying on container classes. This isn't
+ // great, but leaving them where they are in the document creates a large
+ // number of positioning problems with scrollable, absolute, relative,
+ // or overflow hidden containers.
+
+ // Note that we don't actually want to let the user drag it outside the
+ // document. One problem is that doing so lets the user drag objects
+ // infinitely far to the right by dragging them to the edge so the
+ // document extends, scrolling the document, dragging them to the edge
+ // of the new larger document, scrolling the document, and so on forever.
+
+ // To prevent this, we're putting a "frame" (stored in "this._frame") at
+ // top level, then putting the clone inside the frame. The frame has the
+ // same size as the entire viewport, and overflow hidden, so dragging the
+ // item outside the document just cuts it off.
+
+ // Create the clone for dragging.
+ var clone = drag.cloneNode(true);
+
+ pos.setPos(clone);
+ dim.setDim(clone);
+
+ JX.DOM.alterClass(drag, 'drag-dragging', true);
+ JX.DOM.alterClass(clone, 'drag-clone', true);
+
+ var frame = JX.$N('div', {className: 'drag-frame'});
+ frame.appendChild(clone);
+
+ document.body.appendChild(frame);
+
+ this._dragging = drag;
+ this._clone = clone;
+ this._frame = frame;
+
+ var cursor = JX.$V(e);
+ this._offset = new JX.Vector(pos.x - cursor.x, pos.y - cursor.y);
+
+ this.invoke('didBeginDrag', this._dragging);
},
_getTargets : function() {
@@ -195,18 +248,6 @@
var item = items[ii];
var ipos = JX.$V(item);
- if (item == this._dragging) {
- // If the item we're measuring is also the item we're dragging,
- // we need to measure its position as though it was still in the
- // list, not its current position in the document (which is
- // under the cursor). To do this, adjust the measured position by
- // removing the offsets we added to put the item underneath the
- // cursor.
- if (this._lastAdjust) {
- ipos.x -= this._lastAdjust.x;
- ipos.y -= this._lastAdjust.y;
- }
- }
targets.push({
item: items[ii],
@@ -398,39 +439,18 @@
}
}
- // If the drop target indicator is above the cursor in the document,
- // adjust the cursor position for the change in node document position.
- // Do this before choosing a new target to avoid a flash of nonsense.
-
- var scroll = JX.Vector.getAggregateScrollForNode(this._dragging);
-
- var origin = {
- x: this._origin.x + (this._originScroll.x - scroll.x),
- y: this._origin.y + (this._originScroll.y - scroll.y)
- };
+ var f = JX.$V(this._frame);
+ p.x -= f.x;
+ p.y -= f.y;
- var adjust_h = 0;
- var adjust_y = 0;
- if (this._target !== false) {
- var ghost = this.getGhostNode();
- adjust_h = JX.Vector.getDim(ghost).y;
- adjust_y = JX.$V(ghost).y;
-
- if (adjust_y <= origin.y) {
- p.y -= adjust_h;
- }
- }
+ p.y += this._offset.y;
+ this._clone.style.top = p.y + 'px';
if (this._canDragX()) {
- p.x -= origin.x;
- } else {
- p.x = 0;
+ p.x += this._offset.x;
+ this._clone.style.left = p.x + 'px';
}
- p.y -= origin.y;
- this._lastAdjust = new JX.Vector(p.x, p.y);
- p.setPos(this._dragging);
-
e.kill();
},
@@ -444,6 +464,10 @@
var dragging = this._dragging;
this._dragging = null;
+ JX.DOM.remove(this._frame);
+ this._frame = null;
+ this._clone = null;
+
var target = false;
var ghost = false;
@@ -469,7 +493,6 @@
for (var ii = 0; ii < group.length; ii++) {
JX.DOM.alterClass(group[ii].getRootNode(), 'drag-target-list', false);
group[ii]._clearTarget();
- group[ii]._lastAdjust = null;
}
if (!this.invoke('didEndDrag', dragging).getPrevented()) {
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Nov 12, 12:11 AM (6 d, 19 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6749200
Default Alt Text
D15160.id.diff (12 KB)
Attached To
Mode
D15160: When dragging nodes, clone them
Attached
Detach File
Event Timeline
Log In to Comment