From a101b4ba2ed261ab0ba3eb7b41a5caaecba0c463 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 13 Jan 2014 12:24:13 -0800 Subject: [PATCH] Make the rest of the board drag-and-drop UI mostly work Summary: Ref T1344. Makes the UI/UX a little nicer; still no actual backend stuff. This changes: - When you drop an item onto a different column, the item actually moves. - Empty columns render with a special CSS class now, but no nodes in the list. This cleans up some JS jankiness. I made the "empty" columns have a light blue background for now. We could put some sort of subtle background image in them instead, or some kind of call to action if it's not redundant with other UI. Test Plan: {F101208} Reviewers: chad, btrahan Reviewed By: chad CC: chad, aran Maniphest Tasks: T1344 Differential Revision: https://secure.phabricator.com/D7942 --- resources/celerity/map.php | 50 +++++++++---------- .../PhabricatorProjectBoardController.php | 5 ++ src/view/phui/PHUIObjectItemListView.php | 13 +++++ .../css/phui/phui-object-item-list-view.css | 5 -- webroot/rsrc/css/phui/phui-workpanel-view.css | 9 ++++ .../projects/behavior-project-boards.js | 8 +++ webroot/rsrc/js/core/DraggableList.js | 22 ++++++-- 7 files changed, 77 insertions(+), 35 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 93ed16d0f1..e71cc5e5be 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,7 +7,7 @@ return array( 'names' => array( - 'core.pkg.css' => 'ac7deb21', + 'core.pkg.css' => 'dddca4dc', 'core.pkg.js' => 'c907bd96', 'darkconsole.pkg.js' => 'ca8671ce', 'differential.pkg.css' => '827749c1', @@ -137,7 +137,7 @@ return array( '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' => 'fdd2c06f', + 'rsrc/css/phui/phui-object-item-list-view.css' => 'e8192ada', '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', @@ -145,7 +145,7 @@ return array( 'rsrc/css/phui/phui-status.css' => '2f562399', 'rsrc/css/phui/phui-text.css' => '23e9b4b7', 'rsrc/css/phui/phui-workboard-view.css' => 'bf70dd2e', - 'rsrc/css/phui/phui-workpanel-view.css' => '26f738ce', + 'rsrc/css/phui/phui-workpanel-view.css' => 'ffb31e99', 'rsrc/css/sprite-actions.css' => '4557baf8', 'rsrc/css/sprite-apps-large.css' => 'e37c2ff1', 'rsrc/css/sprite-apps-xlarge.css' => 'db66c878', @@ -392,7 +392,7 @@ return array( '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-boards.js' => '9c9f91ec', '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', @@ -417,7 +417,7 @@ return array( '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' => '5fb99faa', + 'rsrc/js/core/DraggableList.js' => '14824eb5', 'rsrc/js/core/DropdownMenu.js' => '2f6f80f4', 'rsrc/js/core/DropdownMenuItem.js' => '0f386ef4', 'rsrc/js/core/FileUpload.js' => '96713558', @@ -603,7 +603,7 @@ return array( 'javelin-behavior-policy-control' => 'c01153ea', 'javelin-behavior-policy-rule-editor' => '263aeb8c', 'javelin-behavior-ponder-votebox' => '327dbe61', - 'javelin-behavior-project-boards' => 'd4cbe3d5', + 'javelin-behavior-project-boards' => '9c9f91ec', 'javelin-behavior-project-create' => '065227cc', 'javelin-behavior-refresh-csrf' => 'c4b31646', 'javelin-behavior-releeph-preview-branch' => '9eb2cedb', @@ -675,7 +675,7 @@ return array( 'phabricator-countdown-css' => '86b7b0a0', 'phabricator-crumbs-view-css' => '2d9db584', 'phabricator-drag-and-drop-file-upload' => 'ae6abfba', - 'phabricator-draggable-list' => '5fb99faa', + 'phabricator-draggable-list' => '14824eb5', 'phabricator-dropdown-menu' => '2f6f80f4', 'phabricator-fatal-config-template-css' => '25d446d6', 'phabricator-feed-css' => '4716c86f', @@ -743,7 +743,7 @@ return array( 'phui-info-panel-css' => '27ea50a1', 'phui-list-view-css' => '2edb76cf', 'phui-object-box-css' => '4f916b80', - 'phui-object-item-list-view-css' => 'fdd2c06f', + 'phui-object-item-list-view-css' => 'e8192ada', 'phui-pinboard-view-css' => '53c5fca0', 'phui-property-list-view-css' => '354465ae', 'phui-remarkup-preview-css' => '19ad512b', @@ -751,7 +751,7 @@ return array( 'phui-status-list-view-css' => '2f562399', 'phui-text-css' => '23e9b4b7', 'phui-workboard-view-css' => 'bf70dd2e', - 'phui-workpanel-view-css' => '26f738ce', + 'phui-workpanel-view-css' => 'ffb31e99', 'policy-css' => '957ea14c', 'policy-edit-css' => '05cca26a', 'ponder-comment-table-css' => '6cdccea7', @@ -871,6 +871,15 @@ return array( 4 => 'javelin-util', 5 => 'phabricator-shaped-request', ), + '14824eb5' => + array( + 0 => 'javelin-install', + 1 => 'javelin-dom', + 2 => 'javelin-stratcom', + 3 => 'javelin-util', + 4 => 'javelin-vector', + 5 => 'javelin-magical-init', + ), '1693a296' => array( 0 => 'javelin-behavior', @@ -1155,15 +1164,6 @@ return array( 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', @@ -1400,6 +1400,13 @@ return array( 3 => 'javelin-vector', 4 => 'phabricator-hovercard', ), + '9c9f91ec' => + array( + 0 => 'javelin-behavior', + 1 => 'javelin-dom', + 2 => 'javelin-util', + 3 => 'phabricator-draggable-list', + ), '9db3d160' => array( 0 => 'javelin-behavior', @@ -1713,13 +1720,6 @@ return array( 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', diff --git a/src/applications/project/controller/PhabricatorProjectBoardController.php b/src/applications/project/controller/PhabricatorProjectBoardController.php index 0189b99671..e09af03c79 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardController.php @@ -84,6 +84,7 @@ final class PhabricatorProjectBoardController ->setUser($viewer) ->setCards(true) ->setFlush(true) + ->setAllowEmptyList(true) ->addSigil('project-column'); $task_phids = idx($task_map, $column->getPHID(), array()); foreach (array_select_keys($tasks, $task_phids) as $task) { @@ -91,6 +92,10 @@ final class PhabricatorProjectBoardController } $panel->setCards($cards); + if (!$task_phids) { + $cards->addClass('project-column-empty'); + } + $board->addPanel($panel); } diff --git a/src/view/phui/PHUIObjectItemListView.php b/src/view/phui/PHUIObjectItemListView.php index 64cc6dc84d..b57d01a04b 100644 --- a/src/view/phui/PHUIObjectItemListView.php +++ b/src/view/phui/PHUIObjectItemListView.php @@ -10,6 +10,17 @@ final class PHUIObjectItemListView extends AphrontTagView { private $noDataString; private $flush; private $plain; + private $allowEmptyList; + + + public function setAllowEmptyList($allow_empty_list) { + $this->allowEmptyList = $allow_empty_list; + return $this; + } + + public function getAllowEmptyList() { + return $this->allowEmptyList; + } public function setFlush($flush) { $this->flush = $flush; @@ -92,6 +103,8 @@ final class PHUIObjectItemListView extends AphrontTagView { if ($this->items) { $items = $this->items; + } else if ($this->allowEmptyList) { + $items = null; } else { $string = nonempty($this->noDataString, pht('No data.')); $items = id(new AphrontErrorView()) diff --git a/webroot/rsrc/css/phui/phui-object-item-list-view.css b/webroot/rsrc/css/phui/phui-object-item-list-view.css index e33db7301d..9f35c024fc 100644 --- a/webroot/rsrc/css/phui/phui-object-item-list-view.css +++ b/webroot/rsrc/css/phui/phui-object-item-list-view.css @@ -571,8 +571,3 @@ padding-left: 0; padding-top: 0; } - -.drag-target-list { - /* TODO: This is a work in progress. */ - background: red; -} diff --git a/webroot/rsrc/css/phui/phui-workpanel-view.css b/webroot/rsrc/css/phui/phui-workpanel-view.css index 89e41ce1f0..0dd755cdab 100644 --- a/webroot/rsrc/css/phui/phui-workpanel-view.css +++ b/webroot/rsrc/css/phui/phui-workpanel-view.css @@ -56,7 +56,16 @@ width: 300px; } +.phui-workpanel-body .phui-object-item-list-view { + min-height: 54px; +} + .device .aphront-multi-column-outer div.aphront-multi-column-column-outer .phui-workpanel-body { width: auto; } + +.project-column-empty { + /* TODO: Use this to put some kind of reasonable null state in the columns? */ + background: {$red}; +} diff --git a/webroot/rsrc/js/application/projects/behavior-project-boards.js b/webroot/rsrc/js/application/projects/behavior-project-boards.js index 44fc0de7f9..41311ff187 100644 --- a/webroot/rsrc/js/application/projects/behavior-project-boards.js +++ b/webroot/rsrc/js/application/projects/behavior-project-boards.js @@ -12,6 +12,10 @@ JX.behavior('project-boards', function(config) { return JX.DOM.scry(col, 'li', 'project-card'); } + function onupdate(node) { + JX.DOM.alterClass(node, 'project-column-empty', !this.findItems().length); + } + var lists = []; var ii; var cols = JX.DOM.scry(JX.$(config.boardID), 'ul', 'project-column'); @@ -19,6 +23,10 @@ JX.behavior('project-boards', function(config) { for (ii = 0; ii < cols.length; ii++) { var list = new JX.DraggableList('project-card', cols[ii]) .setFindItemsHandler(JX.bind(null, finditems, cols[ii])); + + list.listen('didSend', JX.bind(list, onupdate, cols[ii])); + list.listen('didReceive', JX.bind(list, onupdate, cols[ii])); + lists.push(list); } diff --git a/webroot/rsrc/js/core/DraggableList.js b/webroot/rsrc/js/core/DraggableList.js index 21856409f2..afa2b174fa 100644 --- a/webroot/rsrc/js/core/DraggableList.js +++ b/webroot/rsrc/js/core/DraggableList.js @@ -31,7 +31,9 @@ JX.install('DraggableList', { 'didBeginDrag', 'didCancelDrag', 'didEndDrag', - 'didDrop'], + 'didDrop', + 'didSend', + 'didReceive'], properties : { findItemsHandler : null @@ -378,18 +380,28 @@ JX.install('DraggableList', { return; } - var target = this._target; - var dragging = this._dragging; - var ghost = this.getGhostNode(); + var p = JX.$V(e); + var dragging = this._dragging; this._dragging = null; + var target = false; + var ghost = false; + + var target_list = this._getTargetList(p); + if (target_list) { + target = target_list._target; + ghost = target_list.getGhostNode(); + } + JX.$V(0, 0).setPos(dragging); if (target !== false) { JX.DOM.remove(dragging); JX.DOM.replace(ghost, dragging); - this.invoke('didDrop', dragging, target); + this.invoke('didSend', dragging, target_list); + target_list.invoke('didReceive', dragging, this); + target_list.invoke('didDrop', dragging, target, this); } else { this.invoke('didCancelDrag', dragging); }