Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15285513
D7941.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
15 KB
Referenced Files
None
Subscribers
None
D7941.diff
View Options
Index: resources/celerity/map.php
===================================================================
--- resources/celerity/map.php
+++ resources/celerity/map.php
@@ -7,7 +7,7 @@
return array(
'names' =>
array(
- 'core.pkg.css' => '6d59624c',
+ 'core.pkg.css' => 'ac7deb21',
'core.pkg.js' => 'c907bd96',
'darkconsole.pkg.js' => 'ca8671ce',
'differential.pkg.css' => '827749c1',
@@ -137,7 +137,7 @@
'rsrc/css/phui/phui-info-panel.css' => '27ea50a1',
'rsrc/css/phui/phui-list.css' => '2edb76cf',
'rsrc/css/phui/phui-object-box.css' => '4f916b80',
- 'rsrc/css/phui/phui-object-item-list-view.css' => '642fe6b9',
+ 'rsrc/css/phui/phui-object-item-list-view.css' => 'fdd2c06f',
'rsrc/css/phui/phui-pinboard-view.css' => '53c5fca0',
'rsrc/css/phui/phui-property-list-view.css' => '354465ae',
'rsrc/css/phui/phui-remarkup-preview.css' => '19ad512b',
@@ -392,6 +392,7 @@
'rsrc/js/application/policy/behavior-policy-control.js' => 'c01153ea',
'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '263aeb8c',
'rsrc/js/application/ponder/behavior-votebox.js' => '327dbe61',
+ 'rsrc/js/application/projects/behavior-project-boards.js' => 'd4cbe3d5',
'rsrc/js/application/projects/behavior-project-create.js' => '065227cc',
'rsrc/js/application/releeph/releeph-preview-branch.js' => '9eb2cedb',
'rsrc/js/application/releeph/releeph-request-state-change.js' => 'fe7fc914',
@@ -416,7 +417,7 @@
'rsrc/js/application/uiexample/notification-example.js' => 'c51a6616',
'rsrc/js/core/Busy.js' => '6453c869',
'rsrc/js/core/DragAndDropFileUpload.js' => 'ae6abfba',
- 'rsrc/js/core/DraggableList.js' => '6f5a879c',
+ 'rsrc/js/core/DraggableList.js' => '5fb99faa',
'rsrc/js/core/DropdownMenu.js' => '2f6f80f4',
'rsrc/js/core/DropdownMenuItem.js' => '0f386ef4',
'rsrc/js/core/FileUpload.js' => '96713558',
@@ -602,6 +603,7 @@
'javelin-behavior-policy-control' => 'c01153ea',
'javelin-behavior-policy-rule-editor' => '263aeb8c',
'javelin-behavior-ponder-votebox' => '327dbe61',
+ 'javelin-behavior-project-boards' => 'd4cbe3d5',
'javelin-behavior-project-create' => '065227cc',
'javelin-behavior-refresh-csrf' => 'c4b31646',
'javelin-behavior-releeph-preview-branch' => '9eb2cedb',
@@ -673,7 +675,7 @@
'phabricator-countdown-css' => '86b7b0a0',
'phabricator-crumbs-view-css' => '2d9db584',
'phabricator-drag-and-drop-file-upload' => 'ae6abfba',
- 'phabricator-draggable-list' => '6f5a879c',
+ 'phabricator-draggable-list' => '5fb99faa',
'phabricator-dropdown-menu' => '2f6f80f4',
'phabricator-fatal-config-template-css' => '25d446d6',
'phabricator-feed-css' => '4716c86f',
@@ -741,7 +743,7 @@
'phui-info-panel-css' => '27ea50a1',
'phui-list-view-css' => '2edb76cf',
'phui-object-box-css' => '4f916b80',
- 'phui-object-item-list-view-css' => '642fe6b9',
+ 'phui-object-item-list-view-css' => 'fdd2c06f',
'phui-pinboard-view-css' => '53c5fca0',
'phui-property-list-view-css' => '354465ae',
'phui-remarkup-preview-css' => '19ad512b',
@@ -1153,6 +1155,15 @@
array(
0 => 'javelin-install',
),
+ '5fb99faa' =>
+ array(
+ 0 => 'javelin-install',
+ 1 => 'javelin-dom',
+ 2 => 'javelin-stratcom',
+ 3 => 'javelin-util',
+ 4 => 'javelin-vector',
+ 5 => 'javelin-magical-init',
+ ),
'61d927ec' =>
array(
0 => 'javelin-behavior',
@@ -1192,15 +1203,6 @@
1 => 'javelin-dom',
2 => 'javelin-workflow',
),
- '6f5a879c' =>
- array(
- 0 => 'javelin-install',
- 1 => 'javelin-dom',
- 2 => 'javelin-stratcom',
- 3 => 'javelin-util',
- 4 => 'javelin-vector',
- 5 => 'javelin-magical-init',
- ),
'71755c79' =>
array(
0 => 'javelin-behavior',
@@ -1711,6 +1713,13 @@
1 => 'javelin-dom',
2 => 'javelin-view',
),
+ 'd4cbe3d5' =>
+ array(
+ 0 => 'javelin-behavior',
+ 1 => 'javelin-dom',
+ 2 => 'javelin-util',
+ 3 => 'phabricator-draggable-list',
+ ),
'd6ca6b1c' =>
array(
0 => 'javelin-install',
Index: src/applications/project/controller/PhabricatorProjectBoardController.php
===================================================================
--- src/applications/project/controller/PhabricatorProjectBoardController.php
+++ src/applications/project/controller/PhabricatorProjectBoardController.php
@@ -61,9 +61,18 @@
$task_map[$default_phid][] = $task->getPHID();
}
+ $board_id = celerity_generate_unique_node_id();
+
$board = id(new PHUIWorkboardView())
->setUser($viewer)
- ->setFluidishLayout(true);
+ ->setFluidishLayout(true)
+ ->setID($board_id);
+
+ $this->initBehavior(
+ 'project-boards',
+ array(
+ 'boardID' => $board_id,
+ ));
foreach ($columns as $column) {
$panel = id(new PHUIWorkpanelView())
@@ -74,7 +83,8 @@
$cards = id(new PHUIObjectItemListView())
->setUser($viewer)
->setCards(true)
- ->setFlush(true);
+ ->setFlush(true)
+ ->addSigil('project-column');
$task_phids = idx($task_map, $column->getPHID(), array());
foreach (array_select_keys($tasks, $task_phids) as $task) {
$cards->addItem($this->renderTaskCard($task));
@@ -148,6 +158,7 @@
->setHeader($task->getTitle())
->setGrippable($can_edit)
->setHref('/T'.$task->getID())
+ ->addSigil('project-card')
->addAction(
id(new PHUIListItemView())
->setName(pht('Edit'))
Index: webroot/rsrc/css/phui/phui-object-item-list-view.css
===================================================================
--- webroot/rsrc/css/phui/phui-object-item-list-view.css
+++ webroot/rsrc/css/phui/phui-object-item-list-view.css
@@ -572,3 +572,7 @@
padding-top: 0;
}
+.drag-target-list {
+ /* TODO: This is a work in progress. */
+ background: red;
+}
Index: webroot/rsrc/js/application/projects/behavior-project-boards.js
===================================================================
--- /dev/null
+++ webroot/rsrc/js/application/projects/behavior-project-boards.js
@@ -0,0 +1,29 @@
+/**
+ * @provides javelin-behavior-project-boards
+ * @requires javelin-behavior
+ * javelin-dom
+ * javelin-util
+ * phabricator-draggable-list
+ */
+
+JX.behavior('project-boards', function(config) {
+
+ function finditems(col) {
+ return JX.DOM.scry(col, 'li', 'project-card');
+ }
+
+ var lists = [];
+ var ii;
+ var cols = JX.DOM.scry(JX.$(config.boardID), 'ul', 'project-column');
+
+ for (ii = 0; ii < cols.length; ii++) {
+ var list = new JX.DraggableList('project-card', cols[ii])
+ .setFindItemsHandler(JX.bind(null, finditems, cols[ii]));
+ lists.push(list);
+ }
+
+ for (ii = 0; ii < lists.length; ii++) {
+ lists[ii].setGroup(lists);
+ }
+
+});
Index: webroot/rsrc/js/core/DraggableList.js
===================================================================
--- webroot/rsrc/js/core/DraggableList.js
+++ webroot/rsrc/js/core/DraggableList.js
@@ -14,6 +14,7 @@
construct : function(sigil, root) {
this._sigil = sigil;
this._root = root || document.body;
+ this._group = [this];
// NOTE: Javelin does not dispatch mousemove by default.
JX.enableDispatch(document.body, 'mousemove');
@@ -46,6 +47,11 @@
_dimensions : null,
_ghostHandler : null,
_ghostNode : null,
+ _group : null,
+
+ getRootNode : function() {
+ return this._root;
+ },
setGhostHandler : function(handler) {
this._ghostHandler = handler;
@@ -68,8 +74,41 @@
return this;
},
+ setGroup : function(lists) {
+ var result = [];
+ var need_self = true;
+ for (var ii = 0; ii < lists.length; ii++) {
+ if (lists[ii] == this) {
+ need_self = false;
+ }
+ result.push(lists[ii]);
+ }
+
+ if (need_self) {
+ result.push(this);
+ }
+
+ this._group = result;
+ return this;
+ },
+
+ _canDragX : function() {
+ return this._hasGroup();
+ },
+
+ _hasGroup : function() {
+ return (this._group.length > 1);
+ },
+
_defaultGhostHandler : function(ghost, target) {
- var parent = this._dragging.parentNode;
+ var parent;
+
+ if (!this._hasGroup()) {
+ parent = this._dragging.parentNode;
+ } else {
+ parent = this.getRootNode();
+ }
+
if (target && target.nextSibling) {
parent.insertBefore(ghost, target.nextSibling);
} else if (!target && parent.firstChild) {
@@ -116,6 +155,24 @@
this._origin = JX.$V(e);
this._dimensions = JX.$V(this._dragging);
+ for (var ii = 0; ii < this._group.length; ii++) {
+ this._group[ii]._clearTarget();
+ this._group[ii]._generateTargets();
+ }
+
+ 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';
+ }
+
+ JX.DOM.alterClass(this._dragging, 'drag-dragging', true);
+ }
+ },
+
+ _generateTargets : function() {
var targets = [];
var items = this.findItems();
for (var ii = 0; ii < items.length; ii++) {
@@ -126,30 +183,73 @@
}
targets.sort(function(u, v) { return v.y - u.y; });
this._targets = targets;
- this._target = false;
- if (!this.invoke('didBeginDrag', this._dragging).getPrevented()) {
- var ghost = this.getGhostNode();
- ghost.style.height = JX.Vector.getDim(this._dragging).y + 'px';
- JX.DOM.alterClass(this._dragging, 'drag-dragging', true);
+ return this;
+ },
+
+ _getTargetList : function(p) {
+ var target_list;
+ if (this._hasGroup()) {
+ var group = this._group;
+ for (var ii = 0; ii < group.length; ii++) {
+ var root = group[ii].getRootNode();
+ var rp = JX.$V(root);
+ var rd = JX.Vector.getDim(root);
+
+ var is_target = false;
+ if (p.x >= rp.x && p.y >= rp.y) {
+ if (p.x <= (rp.x + rd.x) && p.y <= (rp.y + rd.y)) {
+ is_target = true;
+ target_list = group[ii];
+ }
+ }
+
+ JX.DOM.alterClass(root, 'drag-target-list', is_target);
+ }
+ } else {
+ target_list = this;
}
+
+ return target_list;
},
- _onmove : function(e) {
- if (!this._dragging) {
- return;
+ _setTarget : function(cur_target) {
+ var ghost = this.getGhostNode();
+ var target = this._target;
+
+ if (cur_target !== target) {
+ this._clearTarget();
+ if (cur_target !== false) {
+ var ok = this.getGhostHandler()(ghost, cur_target);
+ // If the handler returns explicit `false`, prevent the drag.
+ if (ok === false) {
+ cur_target = false;
+ }
+ }
+
+ this._target = cur_target;
+ }
+
+ return this;
+ },
+
+ _clearTarget : function() {
+ var target = this._target;
+ var ghost = this.getGhostNode();
+
+ if (target !== false) {
+ JX.DOM.remove(ghost);
}
+ this._target = false;
+ return this;
+ },
+
+ _getCurrentTarget : function(p) {
var ghost = this.getGhostNode();
var target = this._target;
var targets = this._targets;
var dragging = this._dragging;
- var origin = this._origin;
-
- var p = JX.$V(e);
-
- // Compute the size and position of the drop target indicator, because we
- // need to update our static position computations to account for it.
var adjust_h = JX.Vector.getDim(ghost).y;
var adjust_y = JX.$V(ghost).y;
@@ -187,11 +287,16 @@
// Don't choose the dragged row or its predecessor as targets.
cur_target = targets[ii].item;
- if (cur_target == dragging) {
- cur_target = false;
- }
- if (targets[ii - 1] && targets[ii - 1].item == dragging) {
- cur_target = false;
+ if (!dragging) {
+ // If the item on the cursor isn't from this list, it can't be
+ // dropped onto itself or its predecessor in this list.
+ } else {
+ if (cur_target == dragging) {
+ cur_target = false;
+ }
+ if (targets[ii - 1] && targets[ii - 1].item == dragging) {
+ cur_target = false;
+ }
}
break;
@@ -199,41 +304,42 @@
// If the dragged row is the first row, don't allow it to be dragged
// into the first position, since this operation doesn't make sense.
- if (cur_target === null) {
+ if (dragging && cur_target === null) {
var first_item = targets[targets.length - 1].item;
if (dragging === first_item) {
cur_target = false;
}
}
- // If we've selected a new target, update the UI to show where we're
- // going to drop the row.
+ return cur_target;
+ },
- if (cur_target !== target) {
+ _onmove : function(e) {
+ if (!this._dragging) {
+ return;
+ }
- if (target !== false) {
- JX.DOM.remove(ghost);
- }
+ var p = JX.$V(e);
- if (cur_target !== false) {
- var ok = this.getGhostHandler()(ghost, cur_target);
- // If the handler returns explicit `false`, prevent the drag.
- if (ok === false) {
- cur_target = false;
- }
- }
+ var group = this._group;
+ var target_list = this._getTargetList(p);
- target = cur_target;
+ // Compute the size and position of the drop target indicator, because we
+ // need to update our static position computations to account for it.
- if (target !== false) {
+ var cur_target = false;
+ if (target_list) {
+ cur_target = target_list._getCurrentTarget(p);
+ }
- // If we've changed where the ghost node is, update the adjustments
- // so we accurately reflect document state when we tweak things below.
- // This avoids a flash of bad state as the mouse is dragged upward
- // across the document.
+ // If we've selected a new target, update the UI to show where we're
+ // going to drop the row.
- adjust_h = JX.Vector.getDim(ghost).y;
- adjust_y = JX.$V(ghost).y;
+ for (var ii = 0; ii < group.length; ii++) {
+ if (group[ii] == target_list) {
+ group[ii]._setTarget(cur_target);
+ } else {
+ group[ii]._clearTarget();
}
}
@@ -241,16 +347,28 @@
// adjust the cursor position for the change in node document position.
// Do this before choosing a new target to avoid a flash of nonsense.
- if (target !== false) {
+ var origin = this._origin;
+
+ 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.x = 0;
+ if (this._canDragX()) {
+ p.x -= origin.x;
+ } else {
+ p.x = 0;
+ }
+
p.y -= origin.y;
- p.setPos(dragging);
- this._target = target;
+ p.setPos(this._dragging);
e.kill();
},
@@ -276,6 +394,12 @@
this.invoke('didCancelDrag', dragging);
}
+ var group = this._group;
+ for (var ii = 0; ii < group.length; ii++) {
+ JX.DOM.alterClass(group[ii].getRootNode(), 'drag-target-list', false);
+ group[ii]._clearTarget();
+ }
+
if (!this.invoke('didEndDrag', dragging).getPrevented()) {
JX.DOM.alterClass(dragging, 'drag-dragging', false);
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Mar 5, 2:11 PM (16 h, 40 m ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7225740
Default Alt Text
D7941.diff (15 KB)
Attached To
Mode
D7941: Allow tasks to be dragged-and-dropped between workboard columns (UI only)
Attached
Detach File
Event Timeline
Log In to Comment