Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13979476
D16157.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
18 KB
Referenced Files
None
Subscribers
None
D16157.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' => 'b9e2e1e5',
- 'core.pkg.js' => '80f86a0a',
+ 'core.pkg.css' => 'f577cd20',
+ 'core.pkg.js' => 'f2139810',
'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => 'b3eea3f5',
'differential.pkg.js' => '4b7d8f19',
@@ -124,7 +124,7 @@
'rsrc/css/phui/phui-badge.css' => '3baef8db',
'rsrc/css/phui/phui-big-info-view.css' => 'bd903741',
'rsrc/css/phui/phui-box.css' => '5c8387cf',
- 'rsrc/css/phui/phui-button.css' => 'a64a8de6',
+ 'rsrc/css/phui/phui-button.css' => 'e266e0bc',
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
'rsrc/css/phui/phui-crumbs-view.css' => '6b813619',
'rsrc/css/phui/phui-curtain-view.css' => '7148ae25',
@@ -516,14 +516,15 @@
'rsrc/js/core/behavior-watch-anchor.js' => '9f36c42d',
'rsrc/js/core/behavior-workflow.js' => '0a3f3021',
'rsrc/js/core/phtize.js' => 'd254d646',
- 'rsrc/js/phui/behavior-phui-dropdown-menu.js' => '54733475',
+ 'rsrc/js/phui/behavior-phui-dropdown-menu.js' => '1aa4c968',
'rsrc/js/phui/behavior-phui-file-upload.js' => 'b003d4fb',
'rsrc/js/phui/behavior-phui-object-box-tabs.js' => '2bfa2836',
'rsrc/js/phui/behavior-phui-profile-menu.js' => '12884df9',
+ 'rsrc/js/phui/behavior-phui-submenu.js' => 'a6f7a73b',
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
'rsrc/js/phuix/PHUIXActionView.js' => '8cf6d262',
'rsrc/js/phuix/PHUIXAutocomplete.js' => '9196fb06',
- 'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bd4c8dca',
+ 'rsrc/js/phuix/PHUIXDropdownMenu.js' => '82e270da',
'rsrc/js/phuix/PHUIXFormControl.js' => 'e15869a8',
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
),
@@ -669,11 +670,12 @@
'javelin-behavior-phabricator-watch-anchor' => '9f36c42d',
'javelin-behavior-pholio-mock-edit' => 'bee502c8',
'javelin-behavior-pholio-mock-view' => 'fbe497e7',
- 'javelin-behavior-phui-dropdown-menu' => '54733475',
+ 'javelin-behavior-phui-dropdown-menu' => '1aa4c968',
'javelin-behavior-phui-file-upload' => 'b003d4fb',
'javelin-behavior-phui-hovercards' => 'bcaccd64',
'javelin-behavior-phui-object-box-tabs' => '2bfa2836',
'javelin-behavior-phui-profile-menu' => '12884df9',
+ 'javelin-behavior-phui-submenu' => 'a6f7a73b',
'javelin-behavior-policy-control' => 'd0c516d5',
'javelin-behavior-policy-rule-editor' => '5e9f347c',
'javelin-behavior-project-boards' => '14a1faae',
@@ -822,7 +824,7 @@
'phui-badge-view-css' => '3baef8db',
'phui-big-info-view-css' => 'bd903741',
'phui-box-css' => '5c8387cf',
- 'phui-button-css' => 'a64a8de6',
+ 'phui-button-css' => 'e266e0bc',
'phui-calendar-css' => 'ccabe893',
'phui-calendar-day-css' => 'd1cf6f93',
'phui-calendar-list-css' => '56e6381a',
@@ -870,7 +872,7 @@
'phuix-action-list-view' => 'b5c256b8',
'phuix-action-view' => '8cf6d262',
'phuix-autocomplete' => '9196fb06',
- 'phuix-dropdown-menu' => 'bd4c8dca',
+ 'phuix-dropdown-menu' => '82e270da',
'phuix-form-control-view' => 'e15869a8',
'phuix-icon-view' => 'bff6884b',
'policy-css' => '957ea14c',
@@ -1024,6 +1026,12 @@
'javelin-workflow',
'javelin-workboard-controller',
),
+ '1aa4c968' => array(
+ 'javelin-behavior',
+ 'javelin-stratcom',
+ 'javelin-dom',
+ 'phuix-dropdown-menu',
+ ),
'1ad0a787' => array(
'javelin-install',
'javelin-reactor',
@@ -1275,12 +1283,6 @@
'javelin-leader',
'javelin-json',
),
- 54733475 => array(
- 'javelin-behavior',
- 'javelin-stratcom',
- 'javelin-dom',
- 'phuix-dropdown-menu',
- ),
'54b612ba' => array(
'javelin-color',
'javelin-install',
@@ -1529,6 +1531,13 @@
'javelin-vector',
'javelin-stratcom',
),
+ '82e270da' => array(
+ 'javelin-install',
+ 'javelin-util',
+ 'javelin-dom',
+ 'javelin-vector',
+ 'javelin-stratcom',
+ ),
'834a1173' => array(
'javelin-behavior',
'javelin-scrollbar',
@@ -1693,6 +1702,11 @@
'javelin-uri',
'phabricator-notification',
),
+ 'a6f7a73b' => array(
+ 'javelin-behavior',
+ 'javelin-stratcom',
+ 'javelin-dom',
+ ),
'a80d0378' => array(
'javelin-behavior',
'javelin-stratcom',
@@ -1846,13 +1860,6 @@
'javelin-vector',
'phui-hovercard',
),
- 'bd4c8dca' => array(
- 'javelin-install',
- 'javelin-util',
- 'javelin-dom',
- 'javelin-vector',
- 'javelin-stratcom',
- ),
'bdaf4d04' => array(
'javelin-behavior',
'javelin-dom',
diff --git a/src/applications/base/controller/PhabricatorController.php b/src/applications/base/controller/PhabricatorController.php
--- a/src/applications/base/controller/PhabricatorController.php
+++ b/src/applications/base/controller/PhabricatorController.php
@@ -474,8 +474,11 @@
public function newCurtainView($object) {
$viewer = $this->getViewer();
+ $action_id = celerity_generate_unique_node_id();
+
$action_list = id(new PhabricatorActionListView())
- ->setViewer($viewer);
+ ->setViewer($viewer)
+ ->setID($action_id);
// NOTE: Applications (objects of class PhabricatorApplication) can't
// currently be set here, although they don't need any of the extensions
diff --git a/src/applications/maniphest/controller/ManiphestTaskDetailController.php b/src/applications/maniphest/controller/ManiphestTaskDetailController.php
--- a/src/applications/maniphest/controller/ManiphestTaskDetailController.php
+++ b/src/applications/maniphest/controller/ManiphestTaskDetailController.php
@@ -166,15 +166,6 @@
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
- $curtain->addAction(
- id(new PhabricatorActionView())
- ->setName(pht('Merge Duplicates In'))
- ->setHref("/search/attach/{$phid}/TASK/merge/")
- ->setWorkflow(true)
- ->setIcon('fa-compress')
- ->setDisabled(!$can_edit)
- ->setWorkflow(true));
-
$edit_config = $edit_engine->loadDefaultEditConfiguration();
$can_create = (bool)$edit_config;
@@ -195,23 +186,36 @@
$edit_uri = $this->getApplicationURI($edit_uri);
}
- $curtain->addAction(
- id(new PhabricatorActionView())
- ->setName(pht('Create Subtask'))
- ->setHref($edit_uri)
- ->setIcon('fa-level-down')
- ->setDisabled(!$can_create)
- ->setWorkflow(!$can_create));
+ $task_submenu = array();
+
+ $task_submenu[] = id(new PhabricatorActionView())
+ ->setName(pht('Create Subtask'))
+ ->setHref($edit_uri)
+ ->setIcon('fa-level-down')
+ ->setDisabled(!$can_create)
+ ->setWorkflow(!$can_create);
+
+ $task_submenu[] = id(new PhabricatorActionView())
+ ->setName(pht('Edit Blocking Tasks'))
+ ->setHref("/search/attach/{$phid}/TASK/blocks/")
+ ->setWorkflow(true)
+ ->setIcon('fa-link')
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(true);
+
+ $task_submenu[] = id(new PhabricatorActionView())
+ ->setName(pht('Merge Duplicates In'))
+ ->setHref("/search/attach/{$phid}/TASK/merge/")
+ ->setWorkflow(true)
+ ->setIcon('fa-compress')
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(true);
$curtain->addAction(
id(new PhabricatorActionView())
- ->setName(pht('Edit Blocking Tasks'))
- ->setHref("/search/attach/{$phid}/TASK/blocks/")
- ->setWorkflow(true)
- ->setIcon('fa-link')
- ->setDisabled(!$can_edit)
- ->setWorkflow(true));
-
+ ->setName(pht('Edit Related Tasks...'))
+ ->setIcon('fa-anchor')
+ ->setSubmenu($task_submenu));
$owner_phid = $task->getOwnerPHID();
$author_phid = $task->getAuthorPHID();
diff --git a/src/view/layout/PhabricatorActionListView.php b/src/view/layout/PhabricatorActionListView.php
--- a/src/view/layout/PhabricatorActionListView.php
+++ b/src/view/layout/PhabricatorActionListView.php
@@ -21,6 +21,10 @@
return $this;
}
+ public function getID() {
+ return $this->id;
+ }
+
public function render() {
$viewer = $this->getViewer();
@@ -44,13 +48,20 @@
require_celerity_resource('phabricator-action-list-view-css');
+ $items = array();
+ foreach ($actions as $action) {
+ foreach ($action->getItems() as $item) {
+ $items[] = $item;
+ }
+ }
+
return phutil_tag(
'ul',
array(
'class' => 'phabricator-action-list-view',
'id' => $this->id,
),
- $actions);
+ $items);
}
public function getDropdownMenuMetadata() {
diff --git a/src/view/layout/PhabricatorActionView.php b/src/view/layout/PhabricatorActionView.php
--- a/src/view/layout/PhabricatorActionView.php
+++ b/src/view/layout/PhabricatorActionView.php
@@ -14,6 +14,10 @@
private $metadata;
private $selected;
private $openInNewWindow;
+ private $submenu = array();
+ private $hidden;
+ private $depth;
+ private $id;
public function setSelected($selected) {
$this->selected = $selected;
@@ -95,7 +99,60 @@
return $this->openInNewWindow;
}
+ public function getID() {
+ if (!$this->id) {
+ $this->id = celerity_generate_unique_node_id();
+ }
+ return $this->id;
+ }
+
+ public function setSubmenu(array $submenu) {
+ $this->submenu = $submenu;
+
+ if (!$this->getHref()) {
+ $this->setHref('#');
+ }
+
+ return $this;
+ }
+
+ public function getItems($depth = 0) {
+ $items = array();
+
+ $items[] = $this;
+ foreach ($this->submenu as $action) {
+ foreach ($action->getItems($depth + 1) as $item) {
+ $item
+ ->setHidden(true)
+ ->setDepth($depth + 1);
+
+ $items[] = $item;
+ }
+ }
+
+ return $items;
+ }
+
+ public function setHidden($hidden) {
+ $this->hidden = $hidden;
+ return $this;
+ }
+
+ public function getHidden() {
+ return $this->hidden;
+ }
+
+ public function setDepth($depth) {
+ $this->depth = $depth;
+ return $this;
+ }
+
+ public function getDepth() {
+ return $this->depth;
+ }
+
public function render() {
+ $caret_id = celerity_generate_unique_node_id();
$icon = null;
if ($this->icon) {
@@ -155,6 +212,18 @@
$target = null;
}
+ if ($this->submenu) {
+ $caret = javelin_tag(
+ 'span',
+ array(
+ 'class' => 'caret-right',
+ 'id' => $caret_id,
+ ),
+ '');
+ } else {
+ $caret = null;
+ }
+
$item = javelin_tag(
'a',
array(
@@ -164,7 +233,7 @@
'sigil' => $sigils,
'meta' => $this->metadata,
),
- array($icon, $this->name));
+ array($icon, $this->name, $caret));
}
} else {
$item = phutil_tag(
@@ -190,10 +259,47 @@
$classes[] = 'phabricator-action-view-selected';
}
- return phutil_tag(
+ if ($this->submenu) {
+ $classes[] = 'phabricator-action-view-submenu';
+ }
+
+ $style = array();
+
+ if ($this->hidden) {
+ $style[] = 'display: none;';
+ }
+
+ if ($this->depth) {
+ $indent = ($this->depth * 16);
+ $style[] = "margin-left: {$indent}px;";
+ }
+
+ $sigil = null;
+ $meta = null;
+
+ if ($this->submenu) {
+ Javelin::initBehavior('phui-submenu');
+ $sigil = 'phui-submenu';
+
+ $item_ids = array();
+ foreach ($this->submenu as $subitem) {
+ $item_ids[] = $subitem->getID();
+ }
+
+ $meta = array(
+ 'itemIDs' => $item_ids,
+ 'caretID' => $caret_id,
+ );
+ }
+
+ return javelin_tag(
'li',
array(
+ 'id' => $this->getID(),
'class' => implode(' ', $classes),
+ 'style' => implode(' ', $style),
+ 'sigil' => $sigil,
+ 'meta' => $meta,
),
$item);
}
diff --git a/src/view/phui/PHUIButtonView.php b/src/view/phui/PHUIButtonView.php
--- a/src/view/phui/PHUIButtonView.php
+++ b/src/view/phui/PHUIButtonView.php
@@ -110,6 +110,18 @@
return $this;
}
+ public function setDropdownMenuID($id) {
+ Javelin::initBehavior('phui-dropdown-menu');
+
+ $this->addSigil('phui-dropdown-menu');
+ $this->setMetadata(
+ array(
+ 'menuID' => $id,
+ ));
+
+ return $this;
+ }
+
protected function getTagAttributes() {
require_celerity_resource('phui-button-css');
diff --git a/src/view/phui/PHUIHeaderView.php b/src/view/phui/PHUIHeaderView.php
--- a/src/view/phui/PHUIHeaderView.php
+++ b/src/view/phui/PHUIHeaderView.php
@@ -24,6 +24,7 @@
private $badges = array();
private $href;
private $actionList;
+ private $actionListID;
public function setHeader($header) {
$this->header = $header;
@@ -90,6 +91,11 @@
return $this;
}
+ public function setActionListID($action_list_id) {
+ $this->actionListID = $action_list_id;
+ return $this;
+ }
+
public function setPolicyObject(PhabricatorPolicyInterface $object) {
$this->policyObject = $object;
return $this;
@@ -189,14 +195,20 @@
protected function getTagContent() {
- if ($this->actionList) {
+ if ($this->actionList || $this->actionListID) {
$action_button = id(new PHUIButtonView())
->setTag('a')
->setText(pht('Actions'))
->setHref('#')
->setIcon('fa-bars')
- ->addClass('phui-mobile-menu')
- ->setDropdownMenu($this->actionList);
+ ->addClass('phui-mobile-menu');
+
+ if ($this->actionList) {
+ $action_button->setDropdownMenu($this->actionList);
+ } else if ($this->actionListID) {
+ $action_button->setDropdownMenuID($this->actionListID);
+ }
+
$this->addActionLink($action_button);
}
diff --git a/src/view/phui/PHUITimelineEventView.php b/src/view/phui/PHUITimelineEventView.php
--- a/src/view/phui/PHUITimelineEventView.php
+++ b/src/view/phui/PHUITimelineEventView.php
@@ -327,9 +327,7 @@
'sigil' => $sigil,
'aria-haspopup' => 'true',
'aria-expanded' => 'false',
- 'meta' => array(
- 'items' => hsprintf('%s', $action_list),
- ),
+ 'meta' => $action_list->getDropdownMenuMetadata(),
),
array(
$aural,
diff --git a/src/view/phui/PHUITwoColumnView.php b/src/view/phui/PHUITwoColumnView.php
--- a/src/view/phui/PHUITwoColumnView.php
+++ b/src/view/phui/PHUITwoColumnView.php
@@ -114,7 +114,7 @@
$curtain = $this->getCurtain();
if ($curtain) {
$action_list = $curtain->getActionList();
- $this->header->setActionList($action_list);
+ $this->header->setActionListID($action_list->getID());
}
$header = phutil_tag_div(
diff --git a/webroot/rsrc/css/phui/phui-button.css b/webroot/rsrc/css/phui/phui-button.css
--- a/webroot/rsrc/css/phui/phui-button.css
+++ b/webroot/rsrc/css/phui/phui-button.css
@@ -264,6 +264,42 @@
border-top-color: #000;
}
+.phabricator-action-view-submenu .caret-right {
+ float: right;
+ margin-top: 4px;
+ margin-right: 6px;
+ border-left-color: {$lightgreytext};
+}
+
+.phabricator-action-view-submenu .caret {
+ float: right;
+ margin-top: 5px;
+ margin-right: 4px;
+ border-top: 7px solid {$lightgreytext};
+}
+
+.phabricator-action-view-submenu.phui-submenu-open {
+ background: {$greybackground};
+}
+
+.phui-submenu-animate {
+ animation: phui-submenu-summon 0.25s;
+}
+
+@keyframes phui-submenu-summon {
+ 0% {
+ color: {$lightgreytext};
+ margin-left: 0;
+ transform: rotate(12deg);
+ }
+ 60% {
+ margin-left: 24px;
+ transform: rotate(-5deg);
+ margin-top: 18px;
+ }
+}
+
+
/* Icons */
.button.has-icon {
position: relative;
diff --git a/webroot/rsrc/js/phui/behavior-phui-dropdown-menu.js b/webroot/rsrc/js/phui/behavior-phui-dropdown-menu.js
--- a/webroot/rsrc/js/phui/behavior-phui-dropdown-menu.js
+++ b/webroot/rsrc/js/phui/behavior-phui-dropdown-menu.js
@@ -16,17 +16,42 @@
e.kill();
- var list = JX.$H(data.items).getFragment().firstChild;
+ var list;
+ var placeholder;
+ if (data.items) {
+ list = JX.$H(data.items).getFragment().firstChild;
+ } else {
+ list = JX.$(data.menuID);
+ placeholder = JX.$N('span');
+ }
var icon = e.getNode('phui-dropdown-menu');
data.menu = new JX.PHUIXDropdownMenu(icon);
- data.menu.setContent(list);
+
+ data.menu.listen('open', function() {
+ if (placeholder) {
+ JX.DOM.replace(list, placeholder);
+ }
+ data.menu.setContent(list);
+ });
+
+ data.menu.listen('close', function() {
+ if (placeholder) {
+ JX.DOM.replace(placeholder, list);
+ }
+ });
+
data.menu.open();
JX.DOM.listen(list, 'click', 'tag:a', function(e) {
if (!e.isNormalClick()) {
return;
}
+
+ if (JX.Stratcom.pass()) {
+ return;
+ }
+
data.menu.close();
});
});
diff --git a/webroot/rsrc/js/phui/behavior-phui-submenu.js b/webroot/rsrc/js/phui/behavior-phui-submenu.js
new file mode 100644
--- /dev/null
+++ b/webroot/rsrc/js/phui/behavior-phui-submenu.js
@@ -0,0 +1,44 @@
+/**
+ * @provides javelin-behavior-phui-submenu
+ * @requires javelin-behavior
+ * javelin-stratcom
+ * javelin-dom
+ */
+
+JX.behavior('phui-submenu', function() {
+
+ JX.Stratcom.listen('click', 'phui-submenu', function(e) {
+ if (!e.isNormalClick()) {
+ return;
+ }
+
+ var node = e.getNode('phui-submenu');
+ var data = e.getNodeData('phui-submenu');
+
+ e.kill();
+
+ data.open = !data.open;
+
+ for (var ii = 0; ii < data.itemIDs.length; ii++) {
+ var id = data.itemIDs[ii];
+ var item = JX.$(id);
+ if (data.open) {
+ JX.DOM.show(item);
+ } else {
+ JX.DOM.hide(item);
+ }
+
+ // Add a class so we can animate zany effects.
+ JX.DOM.alterClass(item, 'phui-submenu-animate', data.open);
+ }
+
+ JX.DOM.alterClass(node, 'phui-submenu-open', data.open);
+
+ // Toggle the caret from ">" to "V" when opening the menu, and back again
+ // when closing it.
+ var caret = JX.$(data.caretID);
+ JX.DOM.alterClass(caret, 'caret', data.open);
+ JX.DOM.alterClass(caret, 'caret-right', !data.open);
+ });
+
+});
diff --git a/webroot/rsrc/js/phuix/PHUIXDropdownMenu.js b/webroot/rsrc/js/phuix/PHUIXDropdownMenu.js
--- a/webroot/rsrc/js/phuix/PHUIXDropdownMenu.js
+++ b/webroot/rsrc/js/phuix/PHUIXDropdownMenu.js
@@ -42,7 +42,7 @@
JX.Stratcom.listen('keydown', null, JX.bind(this, this._onkey));
},
- events: ['open'],
+ events: ['open', 'close'],
properties: {
width: null,
@@ -83,6 +83,8 @@
this._open = false;
this._hide();
+ this.invoke('close');
+
return this;
},
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Oct 20, 4:47 AM (4 w, 1 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6735161
Default Alt Text
D16157.id.diff (18 KB)
Attached To
Mode
D16157: Fold task-relationship actions into an accordion dropdown
Attached
Detach File
Event Timeline
Log In to Comment