diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -415,7 +415,7 @@ 'rsrc/js/application/policy/behavior-policy-rule-editor.js' => 'fe9a552f', 'rsrc/js/application/ponder/behavior-votebox.js' => '4e9b766b', 'rsrc/js/application/projects/behavior-boards-dropdown.js' => '0ec56e1d', - 'rsrc/js/application/projects/behavior-project-boards.js' => 'd4bf1f3c', + 'rsrc/js/application/projects/behavior-project-boards.js' => 'e4b6c65a', 'rsrc/js/application/projects/behavior-project-create.js' => '065227cc', 'rsrc/js/application/projects/behavior-reorder-columns.js' => 'e1d25dfb', 'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf', @@ -639,7 +639,7 @@ 'javelin-behavior-policy-control' => 'f3fef818', 'javelin-behavior-policy-rule-editor' => 'fe9a552f', 'javelin-behavior-ponder-votebox' => '4e9b766b', - 'javelin-behavior-project-boards' => 'd4bf1f3c', + 'javelin-behavior-project-boards' => 'e4b6c65a', 'javelin-behavior-project-create' => '065227cc', 'javelin-behavior-refresh-csrf' => '7814b593', 'javelin-behavior-releeph-preview-branch' => 'b2b4fbaf', @@ -1693,14 +1693,6 @@ 'javelin-dom', 'javelin-view', ), - 'd4bf1f3c' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-workflow', - 'phabricator-draggable-list', - ), 'd4eecc63' => array( 'javelin-behavior', 'javelin-dom', @@ -1782,6 +1774,14 @@ 'javelin-dom', 'javelin-uri', ), + 'e4b6c65a' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-workflow', + 'phabricator-draggable-list', + ), 'e566f52c' => array( 'javelin-behavior', 'javelin-stratcom', diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php --- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php @@ -220,6 +220,9 @@ $this->handles = ManiphestTaskListView::loadTaskHandles($viewer, $tasks); foreach ($columns as $column) { + $task_phids = idx($task_map, $column->getPHID(), array()); + $column_tasks = array_select_keys($tasks, $task_phids); + $panel = id(new PHUIWorkpanelView()) ->setHeader($column->getDisplayName()) ->setHeaderColor($column->getHeaderColor()); @@ -227,6 +230,18 @@ $column_menu = $this->buildColumnMenu($project, $column); $panel->addHeaderAction($column_menu); + $tag_id = celerity_generate_unique_node_id(); + $tag_content_id = celerity_generate_unique_node_id(); + + $count_tag = id(new PHUITagView()) + ->setType(PHUITagView::TYPE_SHADE) + ->setShade(PHUITagView::COLOR_BLUE) + ->setID($tag_id) + ->setName(phutil_tag('span', array('id' => $tag_content_id), '-')) + ->setStyle('display: none'); + + $panel->setHeaderTag($count_tag); + $cards = id(new PHUIObjectItemListView()) ->setUser($viewer) ->setFlush(true) @@ -235,10 +250,11 @@ ->setMetadata( array( 'columnPHID' => $column->getPHID(), + 'countTagID' => $tag_id, + 'countTagContentID' => $tag_content_id, )); - $task_phids = idx($task_map, $column->getPHID(), array()); - foreach (array_select_keys($tasks, $task_phids) as $task) { + foreach ($column_tasks as $task) { $owner = null; if ($task->getOwnerPHID()) { $owner = $this->handles[$task->getOwnerPHID()]; @@ -253,7 +269,7 @@ } $panel->setCards($cards); - if (!$task_phids) { + if (!$column_tasks) { $cards->addClass('project-column-empty'); } diff --git a/src/applications/project/view/ProjectBoardTaskCard.php b/src/applications/project/view/ProjectBoardTaskCard.php --- a/src/applications/project/view/ProjectBoardTaskCard.php +++ b/src/applications/project/view/ProjectBoardTaskCard.php @@ -35,6 +35,7 @@ $this->canEdit = $can_edit; return $this; } + public function getCanEdit() { return $this->canEdit; } diff --git a/src/view/phui/PHUIWorkpanelView.php b/src/view/phui/PHUIWorkpanelView.php --- a/src/view/phui/PHUIWorkpanelView.php +++ b/src/view/phui/PHUIWorkpanelView.php @@ -7,6 +7,7 @@ private $footerAction; private $headerColor = PHUIActionHeaderView::HEADER_GREY; private $headerActions = array(); + private $headerTag; public function setCards(PHUIObjectItemListView $cards) { $this->cards[] = $cards; @@ -33,6 +34,11 @@ return $this; } + public function setHeaderTag(PHUITagView $tag) { + $this->headerTag = $tag; + return $this; + } + public function getTagAttributes() { return array( 'class' => 'phui-workpanel-view', @@ -59,6 +65,10 @@ ->setHeaderTitle($this->header) ->setHeaderColor($this->headerColor); + if ($this->headerTag) { + $header->setTag($this->headerTag); + } + foreach ($this->headerActions as $action) { $header->addAction($action); } diff --git a/webroot/rsrc/js/application/projects/behavior-project-boards.js b/webroot/rsrc/js/application/projects/behavior-project-boards.js --- a/webroot/rsrc/js/application/projects/behavior-project-boards.js +++ b/webroot/rsrc/js/application/projects/behavior-project-boards.js @@ -14,8 +14,37 @@ return JX.DOM.scry(col, 'li', 'project-card'); } - function onupdate(node) { - JX.DOM.alterClass(node, 'project-column-empty', !this.findItems().length); + function onupdate(col) { + var data = JX.Stratcom.getData(col); + var cards = finditems(col); + + // Add the "empty" CSS class if the column has nothing in it. + JX.DOM.alterClass(col, 'project-column-empty', !cards.length); + + // Update the count of tasks in the column header. + if (!data.countTagNode) { + data.countTagNode = JX.$(data.countTagID); + JX.DOM.show(data.countTagNode); + } + + var sum = 0; + for (var ii = 0; ii < cards.length; ii++) { + // TODO: Allow this to be computed in some more clever way. + sum += 1; + } + + JX.DOM.setContent(JX.$(data.countTagContentID), sum); + + // TODO: This is a little bit hacky, but we don't have a PHUIX version of + // this element yet. + + var color_map = { + 'phui-tag-shade-disabled': (sum === 0), + 'phui-tag-shade-blue': (sum > 0) + }; + for (var k in color_map) { + JX.DOM.alterClass(data.countTagNode, k, color_map[k]); + } } function onresponse(response, item, list) { @@ -24,6 +53,10 @@ JX.DOM.replace(item, JX.$H(response.task)); } + function getcolumns() { + return JX.DOM.scry(JX.$(config.boardID), 'ul', 'project-column'); + } + function colsort(u, v) { var ud = JX.Stratcom.getData(u).sort || []; var vd = JX.Stratcom.getData(v).sort || []; @@ -93,7 +126,7 @@ var lists = []; var ii; - var cols = JX.DOM.scry(JX.$(config.boardID), 'ul', 'project-column'); + var cols = getcolumns(); for (ii = 0; ii < cols.length; ii++) { var list = new JX.DraggableList('project-card', cols[ii]) @@ -105,6 +138,8 @@ list.listen('didDrop', JX.bind(null, ondrop, list)); lists.push(list); + + onupdate(cols[ii]); } for (ii = 0; ii < lists.length; ii++) { @@ -141,6 +176,8 @@ items.sort(colsort); JX.DOM.setContent(column, items); + + onupdate(column); }; JX.Stratcom.listen( @@ -175,7 +212,7 @@ projects: config.projectPHID, order: config.order }; - var cols = JX.DOM.scry(JX.$(config.boardID), 'ul', 'project-column'); + var cols = getcolumns(); var ii; var column; for (ii = 0; ii < cols.length; ii++) { @@ -188,4 +225,5 @@ .setHandler(JX.bind(null, onedit, column)) .start(); }); + });