diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index c54314f0f8..e947ff08c8 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -2318,6 +2318,18 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/application/pholio/behavior-pholio-mock-view.js', ), + 'javelin-behavior-phui-object-box-tabs' => + array( + 'uri' => '/res/c2318be8/rsrc/js/phui/behavior-phui-object-box-tabs.js', + 'type' => 'js', + 'requires' => + array( + 0 => 'javelin-behavior', + 1 => 'javelin-stratcom', + 2 => 'javelin-dom', + ), + 'disk' => '/rsrc/js/phui/behavior-phui-object-box-tabs.js', + ), 'javelin-behavior-policy-control' => array( 'uri' => '/res/ce9f54c8/rsrc/js/application/policy/behavior-policy-control.js', diff --git a/src/applications/uiexample/examples/PHUIPropertyListExample.php b/src/applications/uiexample/examples/PHUIPropertyListExample.php index 3a034f7ae2..5daf821390 100644 --- a/src/applications/uiexample/examples/PHUIPropertyListExample.php +++ b/src/applications/uiexample/examples/PHUIPropertyListExample.php @@ -17,19 +17,16 @@ final class PHUIPropertyListExample extends PhabricatorUIExample { $details1 = id(new PHUIListItemView()) ->setName('Details') - ->setHref('#') ->setSelected(true) ->setType(PHUIListItemView::TYPE_LINK); $details2 = id(new PHUIListItemView()) - ->setName('Lint (Warn)') - ->setHref('#') + ->setName('Rainbow Info') ->setStatusColor(PHUIListItemView::STATUS_WARN) ->setType(PHUIListItemView::TYPE_LINK); $details3 = id(new PHUIListItemView()) - ->setName('Unit (3/5)') - ->setHref('#') + ->setName('Pasta Haiku') ->setStatusColor(PHUIListItemView::STATUS_FAIL) ->setType(PHUIListItemView::TYPE_LINK); @@ -40,7 +37,6 @@ final class PHUIPropertyListExample extends PhabricatorUIExample { ->addMenuItem($details3); $view = new PHUIPropertyListView(); - $view->setTabs($statustabs); $view->addProperty( pht('Color'), @@ -84,9 +80,9 @@ final class PHUIPropertyListExample extends PhabricatorUIExample { $object_box1 = id(new PHUIObjectBoxView()) ->setHeaderText('PHUIPropertyListView Stackered') - ->addPropertyList($view) - ->addPropertyList($view2) - ->addPropertyList($view3); + ->addPropertyList($view, $details1) + ->addPropertyList($view2, $details2) + ->addPropertyList($view3, $details3); $edge_cases_view = new PHUIPropertyListView(); diff --git a/src/view/phui/PHUIObjectBoxView.php b/src/view/phui/PHUIObjectBoxView.php index eeda07297c..8be3ee364d 100644 --- a/src/view/phui/PHUIObjectBoxView.php +++ b/src/view/phui/PHUIObjectBoxView.php @@ -8,12 +8,59 @@ final class PHUIObjectBoxView extends AphrontView { private $validationException; private $header; private $flush; - private $propertyList = array(); - // This is mostly a conveinence method to lessen code dupe - // when building objectboxes. - public function addPropertyList(PHUIPropertyListView $property_list) { - $this->propertyList[] = $property_list; + private $tabs = array(); + private $propertyLists = array(); + private $selectedTab; + + public function addPropertyList( + PHUIPropertyListView $property_list, + PHUIListItemView $tab = null) { + + if ($this->propertyLists) { + $already_has_tabs = (bool)$this->tabs; + $adding_new_tab = (bool)$tab; + + if ($already_has_tabs xor $adding_new_tab) { + throw new Exception( + "You can not mix tabbed and un-tabbed property lists in the same ". + "BoxView."); + } + } + + if ($tab) { + if ($tab->getKey()) { + $key = $tab->getKey(); + } else { + $key = 'tab.default.'.spl_object_hash($tab); + $tab->setKey($key); + } + } else { + $key = 'tab.default'; + } + + if ($tab) { + if (!$this->tabs) { + $this->selectedTab = $key; + } + + if (empty($this->tabs[$key])) { + $tab->addSigil('phui-object-box-tab'); + $tab->setMetadata( + array( + 'tabKey' => $key, + )); + + if (!$tab->getHref()) { + $tab->setHref('#'); + } + + $this->tabs[$key] = $tab; + } + } + + $this->propertyLists[$key][] = $property_list; + return $this; } @@ -74,12 +121,45 @@ final class PHUIObjectBoxView extends AphrontView { } } - $property_list = null; - if ($this->propertyList) { - $property_list = new PHUIPropertyGroupView(); - foreach ($this->propertyList as $item) { - $property_list->addPropertyList($item); + $property_lists = array(); + $tab_map = array(); + foreach ($this->propertyLists as $key => $list) { + $group = new PHUIPropertyGroupView(); + foreach ($list as $item) { + $group->addPropertyList($item); } + + if ($this->tabs) { + $tab_id = celerity_generate_unique_node_id(); + $tab_map[$key] = $tab_id; + + if ($key === $this->selectedTab) { + $style = null; + } else { + $style = 'display: none'; + } + + $property_lists[] = phutil_tag( + 'div', + array( + 'style' => $style, + 'id' => $tab_id, + ), + $group); + } else { + $property_lists[] = $group; + } + } + + $tabs = null; + if ($this->tabs) { + $tabs = id(new PHUIListView()) + ->setType(PHUIListView::NAVBAR_LIST); + foreach ($this->tabs as $tab) { + $tabs->addMenuItem($tab); + } + + Javelin::initBehavior('phui-object-box-tabs'); } $content = id(new PHUIBoxView()) @@ -89,7 +169,8 @@ final class PHUIObjectBoxView extends AphrontView { $this->formError, $exception_errors, $this->form, - $property_list, + $tabs, + $property_lists, $this->renderChildren(), )) ->setBorder(true) @@ -98,6 +179,14 @@ final class PHUIObjectBoxView extends AphrontView { ->addMargin(PHUI::MARGIN_LARGE_RIGHT) ->addClass('phui-object-box'); + if ($this->tabs) { + $content->addSigil('phui-object-box'); + $content->setMetadata( + array( + 'tabMap' => $tab_map, + )); + } + if ($this->flush) { $content->addClass('phui-object-box-flush'); } diff --git a/src/view/phui/PHUIPropertyListView.php b/src/view/phui/PHUIPropertyListView.php index 2181c7d204..1cf8ab3e89 100644 --- a/src/view/phui/PHUIPropertyListView.php +++ b/src/view/phui/PHUIPropertyListView.php @@ -7,7 +7,6 @@ final class PHUIPropertyListView extends AphrontView { private $object; private $invokedWillRenderEvent; private $actionList; - private $tabs; protected function canAppendChild() { return false; @@ -50,11 +49,6 @@ final class PHUIPropertyListView extends AphrontView { return $this; } - public function setTabs(PHUIListView $tabs) { - $this->tabs = $tabs; - return $tabs; - } - public function addSectionHeader($name) { $this->parts[] = array( 'type' => 'section', @@ -123,7 +117,6 @@ final class PHUIPropertyListView extends AphrontView { 'class' => 'phui-property-list-section', ), array( - $this->tabs, $items, )); } diff --git a/webroot/rsrc/js/phui/behavior-phui-object-box-tabs.js b/webroot/rsrc/js/phui/behavior-phui-object-box-tabs.js new file mode 100644 index 0000000000..4d9d0bf367 --- /dev/null +++ b/webroot/rsrc/js/phui/behavior-phui-object-box-tabs.js @@ -0,0 +1,36 @@ +/** + * @provides javelin-behavior-phui-object-box-tabs + * @requires javelin-behavior + * javelin-stratcom + * javelin-dom + */ + +JX.behavior('phui-object-box-tabs', function() { + + JX.Stratcom.listen( + 'click', + 'phui-object-box-tab', + function (e) { + var key = e.getNodeData('phui-object-box-tab').tabKey; + var map = e.getNodeData('phui-object-box').tabMap; + var tab = e.getNode('phui-object-box-tab'); + + var box = e.getNode('phui-object-box'); + var tabs = JX.DOM.scry(box, 'li', 'phui-object-box-tab'); + for (var ii = 0; ii < tabs.length; ii++) { + JX.DOM.alterClass( + tabs[ii], + 'phui-list-item-selected', + (tabs[ii] == tab)); + } + + for (var k in map) { + if (k == key) { + JX.DOM.show(JX.$(map[k])); + } else { + JX.DOM.hide(JX.$(map[k])); + } + } + }); + +});